mirror of https://github.com/docker/buildx.git
				
				
				
			Merge pull request #1451 from crazy-max/update-buildkit
vendor: update buildkit to master@9624ab4
This commit is contained in:
		
						commit
						47cf72b8ba
					
				| 
						 | 
					@ -13,7 +13,7 @@ import (
 | 
				
			||||||
func TestDefaultContextInitializer(t *testing.T) {
 | 
					func TestDefaultContextInitializer(t *testing.T) {
 | 
				
			||||||
	os.Setenv("KUBECONFIG", "./fixtures/test-kubeconfig")
 | 
						os.Setenv("KUBECONFIG", "./fixtures/test-kubeconfig")
 | 
				
			||||||
	defer os.Unsetenv("KUBECONFIG")
 | 
						defer os.Unsetenv("KUBECONFIG")
 | 
				
			||||||
	ctx, err := command.ResolveDefaultContext(&cliflags.CommonOptions{}, command.DefaultContextStoreConfig())
 | 
						ctx, err := command.ResolveDefaultContext(&cliflags.ClientOptions{}, command.DefaultContextStoreConfig())
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	assert.Equal(t, "default", ctx.Meta.Name)
 | 
						assert.Equal(t, "default", ctx.Meta.Name)
 | 
				
			||||||
	assert.Equal(t, "zoinx", ctx.Meta.Endpoints[KubernetesEndpoint].(EndpointMeta).DefaultNamespace)
 | 
						assert.Equal(t, "zoinx", ctx.Meta.Endpoints[KubernetesEndpoint].(EndpointMeta).DefaultNamespace)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										18
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -10,13 +10,13 @@ require (
 | 
				
			||||||
	github.com/docker/cli v20.10.21+incompatible // v22.06.x - see "replace" for the actual version
 | 
						github.com/docker/cli v20.10.21+incompatible // v22.06.x - see "replace" for the actual version
 | 
				
			||||||
	github.com/docker/cli-docs-tool v0.5.0
 | 
						github.com/docker/cli-docs-tool v0.5.0
 | 
				
			||||||
	github.com/docker/distribution v2.8.1+incompatible
 | 
						github.com/docker/distribution v2.8.1+incompatible
 | 
				
			||||||
	github.com/docker/docker v20.10.18+incompatible // v22.06.x - see "replace" for the actual version
 | 
						github.com/docker/docker v20.10.21+incompatible // v22.06.x - see "replace" for the actual version
 | 
				
			||||||
	github.com/docker/go-units v0.5.0
 | 
						github.com/docker/go-units v0.5.0
 | 
				
			||||||
	github.com/gofrs/flock v0.7.3
 | 
						github.com/gofrs/flock v0.8.1
 | 
				
			||||||
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 | 
						github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 | 
				
			||||||
	github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
 | 
						github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
 | 
				
			||||||
	github.com/hashicorp/hcl/v2 v2.8.2
 | 
						github.com/hashicorp/hcl/v2 v2.8.2
 | 
				
			||||||
	github.com/moby/buildkit v0.10.1-0.20221121234933-ae9d0f57c7f3
 | 
						github.com/moby/buildkit v0.11.0-rc1.0.20221205150952-9624ab4710dd
 | 
				
			||||||
	github.com/morikuni/aec v1.0.0
 | 
						github.com/morikuni/aec v1.0.0
 | 
				
			||||||
	github.com/opencontainers/go-digest v1.0.0
 | 
						github.com/opencontainers/go-digest v1.0.0
 | 
				
			||||||
	github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1
 | 
						github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,7 @@ require (
 | 
				
			||||||
	github.com/containerd/typeurl v1.0.2 // indirect
 | 
						github.com/containerd/typeurl v1.0.2 // indirect
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
	github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb // indirect
 | 
						github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb // indirect
 | 
				
			||||||
	github.com/docker/docker-credential-helpers v0.6.4 // indirect
 | 
						github.com/docker/docker-credential-helpers v0.7.0 // indirect
 | 
				
			||||||
	github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
 | 
						github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
 | 
				
			||||||
	github.com/docker/go-connections v0.4.0 // indirect
 | 
						github.com/docker/go-connections v0.4.0 // indirect
 | 
				
			||||||
	github.com/docker/go-metrics v0.0.1 // indirect
 | 
						github.com/docker/go-metrics v0.0.1 // indirect
 | 
				
			||||||
| 
						 | 
					@ -119,7 +119,7 @@ require (
 | 
				
			||||||
	github.com/moby/spdystream v0.2.0 // indirect
 | 
						github.com/moby/spdystream v0.2.0 // indirect
 | 
				
			||||||
	github.com/moby/sys/sequential v0.5.0 // indirect
 | 
						github.com/moby/sys/sequential v0.5.0 // indirect
 | 
				
			||||||
	github.com/moby/sys/signal v0.7.0 // indirect
 | 
						github.com/moby/sys/signal v0.7.0 // indirect
 | 
				
			||||||
	github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
 | 
						github.com/moby/term v0.0.0-20221120202655-abb19827d345 // indirect
 | 
				
			||||||
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
						github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
				
			||||||
	github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
						github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
				
			||||||
	github.com/opencontainers/runc v1.1.3 // indirect
 | 
						github.com/opencontainers/runc v1.1.3 // indirect
 | 
				
			||||||
| 
						 | 
					@ -131,7 +131,7 @@ require (
 | 
				
			||||||
	github.com/rogpeppe/go-internal v1.8.1 // indirect
 | 
						github.com/rogpeppe/go-internal v1.8.1 // indirect
 | 
				
			||||||
	github.com/spf13/viper v1.14.0 // indirect
 | 
						github.com/spf13/viper v1.14.0 // indirect
 | 
				
			||||||
	github.com/theupdateframework/notary v0.6.1 // indirect
 | 
						github.com/theupdateframework/notary v0.6.1 // indirect
 | 
				
			||||||
	github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect
 | 
						github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf // indirect
 | 
				
			||||||
	github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
 | 
						github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
 | 
				
			||||||
	github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
 | 
						github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
 | 
				
			||||||
	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
 | 
						github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
 | 
				
			||||||
| 
						 | 
					@ -153,7 +153,7 @@ require (
 | 
				
			||||||
	golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
 | 
						golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.2.0 // indirect
 | 
						golang.org/x/sys v0.2.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.4.0 // indirect
 | 
						golang.org/x/text v0.4.0 // indirect
 | 
				
			||||||
	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 | 
						golang.org/x/time v0.1.0 // indirect
 | 
				
			||||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
						google.golang.org/appengine v1.6.7 // indirect
 | 
				
			||||||
	google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e // indirect
 | 
						google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e // indirect
 | 
				
			||||||
	google.golang.org/protobuf v1.28.1 // indirect
 | 
						google.golang.org/protobuf v1.28.1 // indirect
 | 
				
			||||||
| 
						 | 
					@ -171,8 +171,8 @@ require (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
replace (
 | 
					replace (
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/config => github.com/aws/aws-sdk-go-v2/config v1.15.5 // same as buildkit
 | 
						github.com/aws/aws-sdk-go-v2/config => github.com/aws/aws-sdk-go-v2/config v1.15.5 // same as buildkit
 | 
				
			||||||
	github.com/docker/cli => github.com/docker/cli v20.10.3-0.20220803220330-418ca3b4d46f+incompatible // master (v22.06-dev)
 | 
						github.com/docker/cli => github.com/docker/cli v20.10.3-0.20221124184145-c0fa00e6142d+incompatible // v23.0.0-dev
 | 
				
			||||||
	github.com/docker/docker => github.com/docker/docker v20.10.3-0.20221006005007-99aa9bb766b5+incompatible // 22.06 branch (v22.06-dev)
 | 
						github.com/docker/docker => github.com/docker/docker v20.10.3-0.20221124164242-a913b5ad7ef1+incompatible // 22.06 branch (v23.0.0-dev)
 | 
				
			||||||
	k8s.io/api => k8s.io/api v0.22.4
 | 
						k8s.io/api => k8s.io/api v0.22.4
 | 
				
			||||||
	k8s.io/apimachinery => k8s.io/apimachinery v0.22.4
 | 
						k8s.io/apimachinery => k8s.io/apimachinery v0.22.4
 | 
				
			||||||
	k8s.io/client-go => k8s.io/client-go v0.22.4
 | 
						k8s.io/client-go => k8s.io/client-go v0.22.4
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										40
									
								
								go.sum
								
								
								
								
							| 
						 | 
					@ -137,7 +137,7 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
 | 
				
			||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
					github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
				
			||||||
github.com/compose-spec/compose-go v1.6.0 h1:7Ol/UULMUtbPmB0EYrETASRoum821JpOh/XaEf+hN+Q=
 | 
					github.com/compose-spec/compose-go v1.6.0 h1:7Ol/UULMUtbPmB0EYrETASRoum821JpOh/XaEf+hN+Q=
 | 
				
			||||||
github.com/compose-spec/compose-go v1.6.0/go.mod h1:os+Ulh2jlZxY1XT1hbciERadjSUU/BtZ6+gcN7vD7J0=
 | 
					github.com/compose-spec/compose-go v1.6.0/go.mod h1:os+Ulh2jlZxY1XT1hbciERadjSUU/BtZ6+gcN7vD7J0=
 | 
				
			||||||
github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4=
 | 
					github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
 | 
				
			||||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
 | 
					github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
 | 
				
			||||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 | 
					github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 | 
				
			||||||
github.com/containerd/containerd v1.6.10 h1:8aiav7I2ZyQLbTlNMcBXyAU1FtFvp6VuyuW13qSd6Hk=
 | 
					github.com/containerd/containerd v1.6.10 h1:8aiav7I2ZyQLbTlNMcBXyAU1FtFvp6VuyuW13qSd6Hk=
 | 
				
			||||||
| 
						 | 
					@ -157,24 +157,22 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
				
			||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
					github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
				
			||||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
 | 
					github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
 | 
				
			||||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
					 | 
				
			||||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 | 
					github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 | 
				
			||||||
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
 | 
					 | 
				
			||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb h1:oCCuuU3kMO3sjZH/p7LamvQNW9SWoT4yQuMGcdSxGAE=
 | 
					github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb h1:oCCuuU3kMO3sjZH/p7LamvQNW9SWoT4yQuMGcdSxGAE=
 | 
				
			||||||
github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb/go.mod h1:28YO/VJk9/64+sTGNuYaBjWxrXTPrj0C0XmgTIOjxX4=
 | 
					github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb/go.mod h1:28YO/VJk9/64+sTGNuYaBjWxrXTPrj0C0XmgTIOjxX4=
 | 
				
			||||||
github.com/docker/cli v20.10.3-0.20220803220330-418ca3b4d46f+incompatible h1:iKanFYBu6Cum7d9j8JGTw2s/d7hUAcXRkEcp2m8b6Qc=
 | 
					github.com/docker/cli v20.10.3-0.20221124184145-c0fa00e6142d+incompatible h1:lbfUnsiVxrzdf/p40pjGV/ZJBVI7kClbzy4DVLLydRA=
 | 
				
			||||||
github.com/docker/cli v20.10.3-0.20220803220330-418ca3b4d46f+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
					github.com/docker/cli v20.10.3-0.20221124184145-c0fa00e6142d+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
				
			||||||
github.com/docker/cli-docs-tool v0.5.0 h1:EjGwI6EyB7YemHCC7R8mwXszJTbuq0T0pFuDC5bMhcE=
 | 
					github.com/docker/cli-docs-tool v0.5.0 h1:EjGwI6EyB7YemHCC7R8mwXszJTbuq0T0pFuDC5bMhcE=
 | 
				
			||||||
github.com/docker/cli-docs-tool v0.5.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
 | 
					github.com/docker/cli-docs-tool v0.5.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
 | 
				
			||||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 | 
					github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 | 
				
			||||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 | 
					github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 | 
				
			||||||
github.com/docker/docker v20.10.3-0.20221006005007-99aa9bb766b5+incompatible h1:lEs5c8XzubP5AhOcbuEljKeqNmszBORctR3BbHsTOuw=
 | 
					github.com/docker/docker v20.10.3-0.20221124164242-a913b5ad7ef1+incompatible h1:DIeHTXiwBnyvC8H38QHJCtU/pyEEAtUHwZgAaDPX2wI=
 | 
				
			||||||
github.com/docker/docker v20.10.3-0.20221006005007-99aa9bb766b5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
					github.com/docker/docker v20.10.3-0.20221124164242-a913b5ad7ef1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
				
			||||||
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
 | 
					github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
 | 
				
			||||||
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
 | 
					github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
 | 
				
			||||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
 | 
					github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
 | 
				
			||||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
 | 
					github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
 | 
				
			||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
					github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
				
			||||||
| 
						 | 
					@ -240,8 +238,8 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
 | 
				
			||||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 | 
					github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 | 
				
			||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
					github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
				
			||||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
					github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
				
			||||||
github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ=
 | 
					github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
 | 
				
			||||||
github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
 | 
					github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
 | 
				
			||||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
 | 
					github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
 | 
				
			||||||
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
 | 
					github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
 | 
				
			||||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
 | 
					github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
 | 
				
			||||||
| 
						 | 
					@ -403,8 +401,8 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
 | 
				
			||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
					github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
				
			||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
					github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
				
			||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 | 
					github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 | 
				
			||||||
github.com/moby/buildkit v0.10.1-0.20221121234933-ae9d0f57c7f3 h1:JDRtokLOyVwiw+8XZagOlS8IbFqqtbmRMnyE/cKdHAg=
 | 
					github.com/moby/buildkit v0.11.0-rc1.0.20221205150952-9624ab4710dd h1:Ap+50rl9e9fkIA02J2Oyy1woX5rXjEeKVSZGznQ4BQI=
 | 
				
			||||||
github.com/moby/buildkit v0.10.1-0.20221121234933-ae9d0f57c7f3/go.mod h1:oDEISQNKfANlbCdk3SM/8BjI+gxryMcxgdb5nGYE9sE=
 | 
					github.com/moby/buildkit v0.11.0-rc1.0.20221205150952-9624ab4710dd/go.mod h1:Bg4hb4FoSQSIht5FyQ6RLri6jEOkZma5Aynsq309UY8=
 | 
				
			||||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
 | 
					github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
 | 
				
			||||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
 | 
					github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
 | 
				
			||||||
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
 | 
					github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
 | 
				
			||||||
| 
						 | 
					@ -417,8 +415,8 @@ github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5
 | 
				
			||||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
 | 
					github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
 | 
				
			||||||
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
 | 
					github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
 | 
				
			||||||
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
 | 
					github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
 | 
				
			||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
 | 
					github.com/moby/term v0.0.0-20221120202655-abb19827d345 h1:J9c53/kxIH+2nTKBEfZYFMlhghtHpIHSXpm5VRGHSnU=
 | 
				
			||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
 | 
					github.com/moby/term v0.0.0-20221120202655-abb19827d345/go.mod h1:15ce4BGCFxt7I5NQKT+HV0yEDxmf6fSysfEDiVo3zFM=
 | 
				
			||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
					github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
				
			||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
					github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
				
			||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
					github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
				
			||||||
| 
						 | 
					@ -526,7 +524,6 @@ github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUq
 | 
				
			||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 | 
					github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 | 
				
			||||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
					github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
				
			||||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
					github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
				
			||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
					 | 
				
			||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 | 
					github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 | 
				
			||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
					github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
				
			||||||
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
 | 
					github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
 | 
				
			||||||
| 
						 | 
					@ -549,8 +546,8 @@ github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs
 | 
				
			||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 | 
					github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 | 
				
			||||||
github.com/theupdateframework/notary v0.6.1 h1:7wshjstgS9x9F5LuB1L5mBI2xNMObWqjz+cjWoom6l0=
 | 
					github.com/theupdateframework/notary v0.6.1 h1:7wshjstgS9x9F5LuB1L5mBI2xNMObWqjz+cjWoom6l0=
 | 
				
			||||||
github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
 | 
					github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
 | 
				
			||||||
github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 h1:NJ1nZs4j4XcBJKIY5sAwTGp9w5b78Zxr3+r0zXRuKnA=
 | 
					github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf h1:2n2v98sRhXEG0Kh7+EvctaNIyOim36Ekp4pGDzbuvO8=
 | 
				
			||||||
github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5/go.mod h1:F83XRhNblQsKQH9hcKEE45GAOkL9590mtw9KsD0Q4fE=
 | 
					github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf/go.mod h1:AvLEd1LEIl64G2Jpgwo7aVV5lGH0ePcKl0ygGIHNYl8=
 | 
				
			||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
 | 
					github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
 | 
				
			||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
 | 
					github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
 | 
				
			||||||
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f h1:DLpt6B5oaaS8jyXHa9VA4rrZloBVPVXeCtrOsrFauxc=
 | 
					github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f h1:DLpt6B5oaaS8jyXHa9VA4rrZloBVPVXeCtrOsrFauxc=
 | 
				
			||||||
| 
						 | 
					@ -770,7 +767,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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-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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
| 
						 | 
					@ -807,8 +803,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
 | 
				
			||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
 | 
					golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
 | 
				
			||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
					golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
				
			||||||
| 
						 | 
					@ -820,7 +816,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
					golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
					golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
					golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
					golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
					golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
					golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
				
			||||||
| 
						 | 
					@ -982,7 +977,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					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.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
 | 
					gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,195 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var bufferPool = &sync.Pool{
 | 
				
			||||||
 | 
						New: func() interface{} {
 | 
				
			||||||
 | 
							buffer := make([]byte, 32*1024)
 | 
				
			||||||
 | 
							return &buffer
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// XAttrErrorHandler transform a non-nil xattr error.
 | 
				
			||||||
 | 
					// Return nil to ignore an error.
 | 
				
			||||||
 | 
					// xattrKey can be empty for listxattr operation.
 | 
				
			||||||
 | 
					type XAttrErrorHandler func(dst, src, xattrKey string, err error) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type copyDirOpts struct {
 | 
				
			||||||
 | 
						xeh XAttrErrorHandler
 | 
				
			||||||
 | 
						// xex contains a set of xattrs to exclude when copying
 | 
				
			||||||
 | 
						xex map[string]struct{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CopyDirOpt func(*copyDirOpts) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithXAttrErrorHandler allows specifying XAttrErrorHandler
 | 
				
			||||||
 | 
					// If nil XAttrErrorHandler is specified (default), CopyDir stops
 | 
				
			||||||
 | 
					// on a non-nil xattr error.
 | 
				
			||||||
 | 
					func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt {
 | 
				
			||||||
 | 
						return func(o *copyDirOpts) error {
 | 
				
			||||||
 | 
							o.xeh = xeh
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAllowXAttrErrors allows ignoring xattr errors.
 | 
				
			||||||
 | 
					func WithAllowXAttrErrors() CopyDirOpt {
 | 
				
			||||||
 | 
						xeh := func(dst, src, xattrKey string, err error) error {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return WithXAttrErrorHandler(xeh)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithXAttrExclude allows for exclusion of specified xattr during CopyDir operation.
 | 
				
			||||||
 | 
					func WithXAttrExclude(keys ...string) CopyDirOpt {
 | 
				
			||||||
 | 
						return func(o *copyDirOpts) error {
 | 
				
			||||||
 | 
							if o.xex == nil {
 | 
				
			||||||
 | 
								o.xex = make(map[string]struct{}, len(keys))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, key := range keys {
 | 
				
			||||||
 | 
								o.xex[key] = struct{}{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CopyDir copies the directory from src to dst.
 | 
				
			||||||
 | 
					// Most efficient copy of files is attempted.
 | 
				
			||||||
 | 
					func CopyDir(dst, src string, opts ...CopyDirOpt) error {
 | 
				
			||||||
 | 
						var o copyDirOpts
 | 
				
			||||||
 | 
						for _, opt := range opts {
 | 
				
			||||||
 | 
							if err := opt(&o); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						inodes := map[uint64]string{}
 | 
				
			||||||
 | 
						return copyDirectory(dst, src, inodes, &o)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
 | 
				
			||||||
 | 
						stat, err := os.Stat(src)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to stat %s: %w", src, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !stat.IsDir() {
 | 
				
			||||||
 | 
							return fmt.Errorf("source %s is not directory", src)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if st, err := os.Stat(dst); err != nil {
 | 
				
			||||||
 | 
							if err := os.Mkdir(dst, stat.Mode()); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to mkdir %s: %w", dst, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if !st.IsDir() {
 | 
				
			||||||
 | 
							return fmt.Errorf("cannot copy to non-directory: %s", dst)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if err := os.Chmod(dst, stat.Mode()); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to chmod on %s: %w", dst, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fis, err := ioutil.ReadDir(src)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to read %s: %w", src, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := copyFileInfo(stat, src, dst); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to copy file info for %s: %w", dst, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := copyXAttrs(dst, src, o.xex, o.xeh); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to copy xattrs: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, fi := range fis {
 | 
				
			||||||
 | 
							source := filepath.Join(src, fi.Name())
 | 
				
			||||||
 | 
							target := filepath.Join(dst, fi.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case fi.IsDir():
 | 
				
			||||||
 | 
								if err := copyDirectory(target, source, inodes, o); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							case (fi.Mode() & os.ModeType) == 0:
 | 
				
			||||||
 | 
								link, err := getLinkSource(target, fi, inodes)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("failed to get hardlink: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if link != "" {
 | 
				
			||||||
 | 
									if err := os.Link(link, target); err != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("failed to create hard link: %w", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if err := CopyFile(target, source); err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("failed to copy files: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink:
 | 
				
			||||||
 | 
								link, err := os.Readlink(source)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("failed to read link: %s: %w", source, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err := os.Symlink(link, target); err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("failed to create symlink: %s: %w", target, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case (fi.Mode() & os.ModeDevice) == os.ModeDevice,
 | 
				
			||||||
 | 
								(fi.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe,
 | 
				
			||||||
 | 
								(fi.Mode() & os.ModeSocket) == os.ModeSocket:
 | 
				
			||||||
 | 
								if err := copyIrregular(target, fi); err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("failed to create irregular file: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								logrus.Warnf("unsupported mode: %s: %s", source, fi.Mode())
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := copyFileInfo(fi, source, target); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to copy file info: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := copyXAttrs(target, source, o.xex, o.xeh); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to copy xattrs: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CopyFile copies the source file to the target.
 | 
				
			||||||
 | 
					// The most efficient means of copying is used for the platform.
 | 
				
			||||||
 | 
					func CopyFile(target, source string) error {
 | 
				
			||||||
 | 
						src, err := os.Open(source)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to open source %s: %w", source, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer src.Close()
 | 
				
			||||||
 | 
						tgt, err := os.Create(target)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to open target %s: %w", target, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer tgt.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return copyFileContent(tgt, src)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								vendor/github.com/containerd/continuity/fs/copy_irregular_freebsd.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										36
									
								
								vendor/github.com/containerd/continuity/fs/copy_irregular_freebsd.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// copyIrregular covers devices, pipes, and sockets
 | 
				
			||||||
 | 
					func copyIrregular(dst string, fi os.FileInfo) error {
 | 
				
			||||||
 | 
						st, ok := fi.Sys().(*syscall.Stat_t) // not *unix.Stat_t
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("unsupported stat type: %s: %v", dst, fi.Mode())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var rDev uint64 // uint64 on FreeBSD, int on other unixen
 | 
				
			||||||
 | 
						if fi.Mode()&os.ModeDevice == os.ModeDevice {
 | 
				
			||||||
 | 
							rDev = st.Rdev
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return syscall.Mknod(dst, uint32(st.Mode), rDev)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					//go:build !windows && !freebsd
 | 
				
			||||||
 | 
					// +build !windows,!freebsd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// copyIrregular covers devices, pipes, and sockets
 | 
				
			||||||
 | 
					func copyIrregular(dst string, fi os.FileInfo) error {
 | 
				
			||||||
 | 
						st, ok := fi.Sys().(*syscall.Stat_t) // not *unix.Stat_t
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("unsupported stat type: %s: %v", dst, fi.Mode())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var rDev int
 | 
				
			||||||
 | 
						if fi.Mode()&os.ModeDevice == os.ModeDevice {
 | 
				
			||||||
 | 
							rDev = int(st.Rdev)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//nolint:unconvert
 | 
				
			||||||
 | 
						return syscall.Mknod(dst, uint32(st.Mode), rDev)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,145 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/continuity/sysx"
 | 
				
			||||||
 | 
						"golang.org/x/sys/unix"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyFileInfo(fi os.FileInfo, src, name string) error {
 | 
				
			||||||
 | 
						st := fi.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
						if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
 | 
				
			||||||
 | 
							if os.IsPermission(err) {
 | 
				
			||||||
 | 
								// Normally if uid/gid are the same this would be a no-op, but some
 | 
				
			||||||
 | 
								// filesystems may still return EPERM... for instance NFS does this.
 | 
				
			||||||
 | 
								// In such a case, this is not an error.
 | 
				
			||||||
 | 
								if dstStat, err2 := os.Lstat(name); err2 == nil {
 | 
				
			||||||
 | 
									st2 := dstStat.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
									if st.Uid == st2.Uid && st.Gid == st2.Gid {
 | 
				
			||||||
 | 
										err = nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to chown %s: %w", name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
 | 
				
			||||||
 | 
							if err := os.Chmod(name, fi.Mode()); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to chmod %s: %w", name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						timespec := []unix.Timespec{
 | 
				
			||||||
 | 
							unix.NsecToTimespec(syscall.TimespecToNsec(StatAtime(st))),
 | 
				
			||||||
 | 
							unix.NsecToTimespec(syscall.TimespecToNsec(StatMtime(st))),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to utime %s: %w", name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const maxSSizeT = int64(^uint(0) >> 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyFileContent(dst, src *os.File) error {
 | 
				
			||||||
 | 
						st, err := src.Stat()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to stat source: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size := st.Size()
 | 
				
			||||||
 | 
						first := true
 | 
				
			||||||
 | 
						srcFd := int(src.Fd())
 | 
				
			||||||
 | 
						dstFd := int(dst.Fd())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for size > 0 {
 | 
				
			||||||
 | 
							// Ensure that we are never trying to copy more than SSIZE_MAX at a
 | 
				
			||||||
 | 
							// time and at the same time avoids overflows when the file is larger
 | 
				
			||||||
 | 
							// than 4GB on 32-bit systems.
 | 
				
			||||||
 | 
							var copySize int
 | 
				
			||||||
 | 
							if size > maxSSizeT {
 | 
				
			||||||
 | 
								copySize = int(maxSSizeT)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								copySize = int(size)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, copySize, 0)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if (err != unix.ENOSYS && err != unix.EXDEV) || !first {
 | 
				
			||||||
 | 
									return fmt.Errorf("copy file range failed: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								buf := bufferPool.Get().(*[]byte)
 | 
				
			||||||
 | 
								_, err = io.CopyBuffer(dst, src, *buf)
 | 
				
			||||||
 | 
								bufferPool.Put(buf)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("userspace copy failed: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							first = false
 | 
				
			||||||
 | 
							size -= int64(n)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
 | 
				
			||||||
 | 
						xattrKeys, err := sysx.LListxattr(src)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
 | 
				
			||||||
 | 
							if errorHandler != nil {
 | 
				
			||||||
 | 
								e = errorHandler(dst, src, "", e)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return e
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, xattr := range xattrKeys {
 | 
				
			||||||
 | 
							if _, exclude := excludes[xattr]; exclude {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							data, err := sysx.LGetxattr(src, xattr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e := fmt.Errorf("failed to get xattr %q on %s: %w", xattr, src, err)
 | 
				
			||||||
 | 
								if errorHandler != nil {
 | 
				
			||||||
 | 
									if e = errorHandler(dst, src, xattr, e); e == nil {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return e
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
 | 
				
			||||||
 | 
								e := fmt.Errorf("failed to set xattr %q on %s: %w", xattr, dst, err)
 | 
				
			||||||
 | 
								if errorHandler != nil {
 | 
				
			||||||
 | 
									if e = errorHandler(dst, src, xattr, e); e == nil {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return e
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,106 @@
 | 
				
			||||||
 | 
					//go:build darwin || freebsd || openbsd || netbsd || solaris
 | 
				
			||||||
 | 
					// +build darwin freebsd openbsd netbsd solaris
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/continuity/sysx"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyFileInfo(fi os.FileInfo, src, name string) error {
 | 
				
			||||||
 | 
						st := fi.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
						if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
 | 
				
			||||||
 | 
							if os.IsPermission(err) {
 | 
				
			||||||
 | 
								// Normally if uid/gid are the same this would be a no-op, but some
 | 
				
			||||||
 | 
								// filesystems may still return EPERM... for instance NFS does this.
 | 
				
			||||||
 | 
								// In such a case, this is not an error.
 | 
				
			||||||
 | 
								if dstStat, err2 := os.Lstat(name); err2 == nil {
 | 
				
			||||||
 | 
									st2 := dstStat.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
									if st.Uid == st2.Uid && st.Gid == st2.Gid {
 | 
				
			||||||
 | 
										err = nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to chown %s: %w", name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
 | 
				
			||||||
 | 
							if err := os.Chmod(name, fi.Mode()); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to chmod %s: %w", name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := utimesNano(name, StatAtime(st), StatMtime(st)); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to utime %s: %w", name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyFileContent(dst, src *os.File) error {
 | 
				
			||||||
 | 
						buf := bufferPool.Get().(*[]byte)
 | 
				
			||||||
 | 
						_, err := io.CopyBuffer(dst, src, *buf)
 | 
				
			||||||
 | 
						bufferPool.Put(buf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
 | 
				
			||||||
 | 
						xattrKeys, err := sysx.LListxattr(src)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
 | 
				
			||||||
 | 
							if errorHandler != nil {
 | 
				
			||||||
 | 
								e = errorHandler(dst, src, "", e)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return e
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, xattr := range xattrKeys {
 | 
				
			||||||
 | 
							if _, exclude := excludes[xattr]; exclude {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							data, err := sysx.LGetxattr(src, xattr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e := fmt.Errorf("failed to get xattr %q on %s: %w", xattr, src, err)
 | 
				
			||||||
 | 
								if errorHandler != nil {
 | 
				
			||||||
 | 
									if e = errorHandler(dst, src, xattr, e); e == nil {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return e
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
 | 
				
			||||||
 | 
								e := fmt.Errorf("failed to set xattr %q on %s: %w", xattr, dst, err)
 | 
				
			||||||
 | 
								if errorHandler != nil {
 | 
				
			||||||
 | 
									if e = errorHandler(dst, src, xattr, e); e == nil {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return e
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,90 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						winio "github.com/Microsoft/go-winio"
 | 
				
			||||||
 | 
						"golang.org/x/sys/windows"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyFileInfo(fi os.FileInfo, src, name string) error {
 | 
				
			||||||
 | 
						if err := os.Chmod(name, fi.Mode()); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to chmod %s: %w", name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy file ownership and ACL
 | 
				
			||||||
 | 
						// We need SeRestorePrivilege and SeTakeOwnershipPrivilege in order
 | 
				
			||||||
 | 
						// to restore security info on a file, especially if we're trying to
 | 
				
			||||||
 | 
						// apply security info which includes SIDs not necessarily present on
 | 
				
			||||||
 | 
						// the host.
 | 
				
			||||||
 | 
						privileges := []string{winio.SeRestorePrivilege, seTakeOwnershipPrivilege}
 | 
				
			||||||
 | 
						if err := winio.EnableProcessPrivileges(privileges); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer winio.DisableProcessPrivileges(privileges)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secInfo, err := windows.GetNamedSecurityInfo(
 | 
				
			||||||
 | 
							src, windows.SE_FILE_OBJECT,
 | 
				
			||||||
 | 
							windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dacl, _, err := secInfo.DACL()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sid, _, err := secInfo.Owner()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := windows.SetNamedSecurityInfo(
 | 
				
			||||||
 | 
							name, windows.SE_FILE_OBJECT,
 | 
				
			||||||
 | 
							windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
 | 
				
			||||||
 | 
							sid, nil, dacl, nil); err != nil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyFileContent(dst, src *os.File) error {
 | 
				
			||||||
 | 
						buf := bufferPool.Get().(*[]byte)
 | 
				
			||||||
 | 
						_, err := io.CopyBuffer(dst, src, *buf)
 | 
				
			||||||
 | 
						bufferPool.Put(buf)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyIrregular(dst string, fi os.FileInfo) error {
 | 
				
			||||||
 | 
						return errors.New("irregular copy not supported")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,325 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						"golang.org/x/sync/errgroup"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ChangeKind is the type of modification that
 | 
				
			||||||
 | 
					// a change is making.
 | 
				
			||||||
 | 
					type ChangeKind int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// ChangeKindUnmodified represents an unmodified
 | 
				
			||||||
 | 
						// file
 | 
				
			||||||
 | 
						ChangeKindUnmodified = iota
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ChangeKindAdd represents an addition of
 | 
				
			||||||
 | 
						// a file
 | 
				
			||||||
 | 
						ChangeKindAdd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ChangeKindModify represents a change to
 | 
				
			||||||
 | 
						// an existing file
 | 
				
			||||||
 | 
						ChangeKindModify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ChangeKindDelete represents a delete of
 | 
				
			||||||
 | 
						// a file
 | 
				
			||||||
 | 
						ChangeKindDelete
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k ChangeKind) String() string {
 | 
				
			||||||
 | 
						switch k {
 | 
				
			||||||
 | 
						case ChangeKindUnmodified:
 | 
				
			||||||
 | 
							return "unmodified"
 | 
				
			||||||
 | 
						case ChangeKindAdd:
 | 
				
			||||||
 | 
							return "add"
 | 
				
			||||||
 | 
						case ChangeKindModify:
 | 
				
			||||||
 | 
							return "modify"
 | 
				
			||||||
 | 
						case ChangeKindDelete:
 | 
				
			||||||
 | 
							return "delete"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Change represents single change between a diff and its parent.
 | 
				
			||||||
 | 
					type Change struct {
 | 
				
			||||||
 | 
						Kind ChangeKind
 | 
				
			||||||
 | 
						Path string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ChangeFunc is the type of function called for each change
 | 
				
			||||||
 | 
					// computed during a directory changes calculation.
 | 
				
			||||||
 | 
					type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Changes computes changes between two directories calling the
 | 
				
			||||||
 | 
					// given change function for each computed change. The first
 | 
				
			||||||
 | 
					// directory is intended to the base directory and second
 | 
				
			||||||
 | 
					// directory the changed directory.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The change callback is called by the order of path names and
 | 
				
			||||||
 | 
					// should be appliable in that order.
 | 
				
			||||||
 | 
					//  Due to this apply ordering, the following is true
 | 
				
			||||||
 | 
					//  - Removed directory trees only create a single change for the root
 | 
				
			||||||
 | 
					//    directory removed. Remaining changes are implied.
 | 
				
			||||||
 | 
					//  - A directory which is modified to become a file will not have
 | 
				
			||||||
 | 
					//    delete entries for sub-path items, their removal is implied
 | 
				
			||||||
 | 
					//    by the removal of the parent directory.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Opaque directories will not be treated specially and each file
 | 
				
			||||||
 | 
					// removed from the base directory will show up as a removal.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// File content comparisons will be done on files which have timestamps
 | 
				
			||||||
 | 
					// which may have been truncated. If either of the files being compared
 | 
				
			||||||
 | 
					// has a zero value nanosecond value, each byte will be compared for
 | 
				
			||||||
 | 
					// differences. If 2 files have the same seconds value but different
 | 
				
			||||||
 | 
					// nanosecond values where one of those values is zero, the files will
 | 
				
			||||||
 | 
					// be considered unchanged if the content is the same. This behavior
 | 
				
			||||||
 | 
					// is to account for timestamp truncation during archiving.
 | 
				
			||||||
 | 
					func Changes(ctx context.Context, a, b string, changeFn ChangeFunc) error {
 | 
				
			||||||
 | 
						if a == "" {
 | 
				
			||||||
 | 
							logrus.Debugf("Using single walk diff for %s", b)
 | 
				
			||||||
 | 
							return addDirChanges(ctx, changeFn, b)
 | 
				
			||||||
 | 
						} else if diffOptions := detectDirDiff(b, a); diffOptions != nil {
 | 
				
			||||||
 | 
							logrus.Debugf("Using single walk diff for %s from %s", diffOptions.diffDir, a)
 | 
				
			||||||
 | 
							return diffDirChanges(ctx, changeFn, a, diffOptions)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logrus.Debugf("Using double walk diff for %s from %s", b, a)
 | 
				
			||||||
 | 
						return doubleWalkDiff(ctx, changeFn, a, b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func addDirChanges(ctx context.Context, changeFn ChangeFunc, root string) error {
 | 
				
			||||||
 | 
						return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Rebase path
 | 
				
			||||||
 | 
							path, err = filepath.Rel(root, path)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							path = filepath.Join(string(os.PathSeparator), path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Skip root
 | 
				
			||||||
 | 
							if path == string(os.PathSeparator) {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return changeFn(ChangeKindAdd, path, f, nil)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// diffDirOptions is used when the diff can be directly calculated from
 | 
				
			||||||
 | 
					// a diff directory to its base, without walking both trees.
 | 
				
			||||||
 | 
					type diffDirOptions struct {
 | 
				
			||||||
 | 
						diffDir      string
 | 
				
			||||||
 | 
						skipChange   func(string) (bool, error)
 | 
				
			||||||
 | 
						deleteChange func(string, string, os.FileInfo) (string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// diffDirChanges walks the diff directory and compares changes against the base.
 | 
				
			||||||
 | 
					func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *diffDirOptions) error {
 | 
				
			||||||
 | 
						changedDirs := make(map[string]struct{})
 | 
				
			||||||
 | 
						return filepath.Walk(o.diffDir, func(path string, f os.FileInfo, err error) error {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Rebase path
 | 
				
			||||||
 | 
							path, err = filepath.Rel(o.diffDir, path)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							path = filepath.Join(string(os.PathSeparator), path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Skip root
 | 
				
			||||||
 | 
							if path == string(os.PathSeparator) {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO: handle opaqueness, start new double walker at this
 | 
				
			||||||
 | 
							// location to get deletes, and skip tree in single walker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if o.skipChange != nil {
 | 
				
			||||||
 | 
								if skip, err := o.skipChange(path); skip {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var kind ChangeKind
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							deletedFile, err := o.deleteChange(o.diffDir, path, f)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Find out what kind of modification happened
 | 
				
			||||||
 | 
							if deletedFile != "" {
 | 
				
			||||||
 | 
								path = deletedFile
 | 
				
			||||||
 | 
								kind = ChangeKindDelete
 | 
				
			||||||
 | 
								f = nil
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Otherwise, the file was added
 | 
				
			||||||
 | 
								kind = ChangeKindAdd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// ...Unless it already existed in a base, in which case, it's a modification
 | 
				
			||||||
 | 
								stat, err := os.Stat(filepath.Join(base, path))
 | 
				
			||||||
 | 
								if err != nil && !os.IsNotExist(err) {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err == nil {
 | 
				
			||||||
 | 
									// The file existed in the base, so that's a modification
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// However, if it's a directory, maybe it wasn't actually modified.
 | 
				
			||||||
 | 
									// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
 | 
				
			||||||
 | 
									if stat.IsDir() && f.IsDir() {
 | 
				
			||||||
 | 
										if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) {
 | 
				
			||||||
 | 
											// Both directories are the same, don't record the change
 | 
				
			||||||
 | 
											return nil
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									kind = ChangeKindModify
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// If /foo/bar/file.txt is modified, then /foo/bar must be part of the changed files.
 | 
				
			||||||
 | 
							// This block is here to ensure the change is recorded even if the
 | 
				
			||||||
 | 
							// modify time, mode and size of the parent directory in the rw and ro layers are all equal.
 | 
				
			||||||
 | 
							// Check https://github.com/docker/docker/pull/13590 for details.
 | 
				
			||||||
 | 
							if f.IsDir() {
 | 
				
			||||||
 | 
								changedDirs[path] = struct{}{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if kind == ChangeKindAdd || kind == ChangeKindDelete {
 | 
				
			||||||
 | 
								parent := filepath.Dir(path)
 | 
				
			||||||
 | 
								if _, ok := changedDirs[parent]; !ok && parent != "/" {
 | 
				
			||||||
 | 
									pi, err := os.Stat(filepath.Join(o.diffDir, parent))
 | 
				
			||||||
 | 
									if err := changeFn(ChangeKindModify, parent, pi, err); err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									changedDirs[parent] = struct{}{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return changeFn(kind, path, f, nil)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// doubleWalkDiff walks both directories to create a diff
 | 
				
			||||||
 | 
					func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err error) {
 | 
				
			||||||
 | 
						g, ctx := errgroup.WithContext(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							c1 = make(chan *currentPath)
 | 
				
			||||||
 | 
							c2 = make(chan *currentPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							f1, f2 *currentPath
 | 
				
			||||||
 | 
							rmdir  string
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						g.Go(func() error {
 | 
				
			||||||
 | 
							defer close(c1)
 | 
				
			||||||
 | 
							return pathWalk(ctx, a, c1)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						g.Go(func() error {
 | 
				
			||||||
 | 
							defer close(c2)
 | 
				
			||||||
 | 
							return pathWalk(ctx, b, c2)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						g.Go(func() error {
 | 
				
			||||||
 | 
							for c1 != nil || c2 != nil {
 | 
				
			||||||
 | 
								if f1 == nil && c1 != nil {
 | 
				
			||||||
 | 
									f1, err = nextPath(ctx, c1)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if f1 == nil {
 | 
				
			||||||
 | 
										c1 = nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if f2 == nil && c2 != nil {
 | 
				
			||||||
 | 
									f2, err = nextPath(ctx, c2)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if f2 == nil {
 | 
				
			||||||
 | 
										c2 = nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if f1 == nil && f2 == nil {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var f os.FileInfo
 | 
				
			||||||
 | 
								k, p := pathChange(f1, f2)
 | 
				
			||||||
 | 
								switch k {
 | 
				
			||||||
 | 
								case ChangeKindAdd:
 | 
				
			||||||
 | 
									if rmdir != "" {
 | 
				
			||||||
 | 
										rmdir = ""
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									f = f2.f
 | 
				
			||||||
 | 
									f2 = nil
 | 
				
			||||||
 | 
								case ChangeKindDelete:
 | 
				
			||||||
 | 
									// Check if this file is already removed by being
 | 
				
			||||||
 | 
									// under of a removed directory
 | 
				
			||||||
 | 
									if rmdir != "" && strings.HasPrefix(f1.path, rmdir) {
 | 
				
			||||||
 | 
										f1 = nil
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									} else if f1.f.IsDir() {
 | 
				
			||||||
 | 
										rmdir = f1.path + string(os.PathSeparator)
 | 
				
			||||||
 | 
									} else if rmdir != "" {
 | 
				
			||||||
 | 
										rmdir = ""
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									f1 = nil
 | 
				
			||||||
 | 
								case ChangeKindModify:
 | 
				
			||||||
 | 
									same, err := sameFile(f1, f2)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if f1.f.IsDir() && !f2.f.IsDir() {
 | 
				
			||||||
 | 
										rmdir = f1.path + string(os.PathSeparator)
 | 
				
			||||||
 | 
									} else if rmdir != "" {
 | 
				
			||||||
 | 
										rmdir = ""
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									f = f2.f
 | 
				
			||||||
 | 
									f1 = nil
 | 
				
			||||||
 | 
									f2 = nil
 | 
				
			||||||
 | 
									if same {
 | 
				
			||||||
 | 
										if !isLinked(f) {
 | 
				
			||||||
 | 
											continue
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										k = ChangeKindUnmodified
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err := changeFn(k, p, f, nil); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return g.Wait()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,75 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/continuity/sysx"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// detectDirDiff returns diff dir options if a directory could
 | 
				
			||||||
 | 
					// be found in the mount info for upper which is the direct
 | 
				
			||||||
 | 
					// diff with the provided lower directory
 | 
				
			||||||
 | 
					func detectDirDiff(upper, lower string) *diffDirOptions {
 | 
				
			||||||
 | 
						// TODO: get mount options for upper
 | 
				
			||||||
 | 
						// TODO: detect AUFS
 | 
				
			||||||
 | 
						// TODO: detect overlay
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// compareSysStat returns whether the stats are equivalent,
 | 
				
			||||||
 | 
					// whether the files are considered the same file, and
 | 
				
			||||||
 | 
					// an error
 | 
				
			||||||
 | 
					func compareSysStat(s1, s2 interface{}) (bool, error) {
 | 
				
			||||||
 | 
						ls1, ok := s1.(*syscall.Stat_t)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ls2, ok := s2.(*syscall.Stat_t)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ls1.Mode == ls2.Mode && ls1.Uid == ls2.Uid && ls1.Gid == ls2.Gid && ls1.Rdev == ls2.Rdev, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func compareCapabilities(p1, p2 string) (bool, error) {
 | 
				
			||||||
 | 
						c1, err := sysx.LGetxattr(p1, "security.capability")
 | 
				
			||||||
 | 
						if err != nil && err != sysx.ENODATA {
 | 
				
			||||||
 | 
							return false, fmt.Errorf("failed to get xattr for %s: %w", p1, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						c2, err := sysx.LGetxattr(p2, "security.capability")
 | 
				
			||||||
 | 
						if err != nil && err != sysx.ENODATA {
 | 
				
			||||||
 | 
							return false, fmt.Errorf("failed to get xattr for %s: %w", p2, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return bytes.Equal(c1, c2), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isLinked(f os.FileInfo) bool {
 | 
				
			||||||
 | 
						s, ok := f.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return !f.IsDir() && s.Nlink > 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/sys/windows"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func detectDirDiff(upper, lower string) *diffDirOptions {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func compareSysStat(s1, s2 interface{}) (bool, error) {
 | 
				
			||||||
 | 
						f1, ok := s1.(windows.Win32FileAttributeData)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						f2, ok := s2.(windows.Win32FileAttributeData)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return f1.FileAttributes == f2.FileAttributes, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func compareCapabilities(p1, p2 string) (bool, error) {
 | 
				
			||||||
 | 
						// TODO: Use windows equivalent
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isLinked(os.FileInfo) bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,104 @@
 | 
				
			||||||
 | 
					//go:build linux
 | 
				
			||||||
 | 
					// +build linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func locateDummyIfEmpty(path string) (string, error) {
 | 
				
			||||||
 | 
						children, err := ioutil.ReadDir(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(children) != 0 {
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dummyFile, err := os.CreateTemp(path, "fsutils-dummy")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						name := dummyFile.Name()
 | 
				
			||||||
 | 
						err = dummyFile.Close()
 | 
				
			||||||
 | 
						return name, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SupportsDType returns whether the filesystem mounted on path supports d_type
 | 
				
			||||||
 | 
					func SupportsDType(path string) (bool, error) {
 | 
				
			||||||
 | 
						// locate dummy so that we have at least one dirent
 | 
				
			||||||
 | 
						dummy, err := locateDummyIfEmpty(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if dummy != "" {
 | 
				
			||||||
 | 
							defer os.Remove(dummy)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						visited := 0
 | 
				
			||||||
 | 
						supportsDType := true
 | 
				
			||||||
 | 
						fn := func(ent *syscall.Dirent) bool {
 | 
				
			||||||
 | 
							visited++
 | 
				
			||||||
 | 
							if ent.Type == syscall.DT_UNKNOWN {
 | 
				
			||||||
 | 
								supportsDType = false
 | 
				
			||||||
 | 
								// stop iteration
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// continue iteration
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = iterateReadDir(path, fn); err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if visited == 0 {
 | 
				
			||||||
 | 
							return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return supportsDType, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
 | 
				
			||||||
 | 
						d, err := os.Open(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer d.Close()
 | 
				
			||||||
 | 
						fd := int(d.Fd())
 | 
				
			||||||
 | 
						buf := make([]byte, 4096)
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							nbytes, err := syscall.ReadDirent(fd, buf)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if nbytes == 0 {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for off := 0; off < nbytes; {
 | 
				
			||||||
 | 
								ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
 | 
				
			||||||
 | 
								if stop := fn(ent); stop {
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								off += int(ent.Reclen)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Usage of disk information
 | 
				
			||||||
 | 
					type Usage struct {
 | 
				
			||||||
 | 
						Inodes int64
 | 
				
			||||||
 | 
						Size   int64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DiskUsage counts the number of inodes and disk usage for the resources under
 | 
				
			||||||
 | 
					// path.
 | 
				
			||||||
 | 
					func DiskUsage(ctx context.Context, roots ...string) (Usage, error) {
 | 
				
			||||||
 | 
						return diskUsage(ctx, roots...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DiffUsage counts the numbers of inodes and disk usage in the
 | 
				
			||||||
 | 
					// diff between the 2 directories. The first path is intended
 | 
				
			||||||
 | 
					// as the base directory and the second as the changed directory.
 | 
				
			||||||
 | 
					func DiffUsage(ctx context.Context, a, b string) (Usage, error) {
 | 
				
			||||||
 | 
						return diffUsage(ctx, a, b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// blocksUnitSize is the unit used by `st_blocks` in `stat` in bytes.
 | 
				
			||||||
 | 
					// See https://man7.org/linux/man-pages/man2/stat.2.html
 | 
				
			||||||
 | 
					//   st_blocks
 | 
				
			||||||
 | 
					//     This field indicates the number of blocks allocated to the
 | 
				
			||||||
 | 
					//     file, in 512-byte units.  (This may be smaller than
 | 
				
			||||||
 | 
					//     st_size/512 when the file has holes.)
 | 
				
			||||||
 | 
					const blocksUnitSize = 512
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type inode struct {
 | 
				
			||||||
 | 
						// TODO(stevvooe): Can probably reduce memory usage by not tracking
 | 
				
			||||||
 | 
						// device, but we can leave this right for now.
 | 
				
			||||||
 | 
						dev, ino uint64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newInode(stat *syscall.Stat_t) inode {
 | 
				
			||||||
 | 
						return inode{
 | 
				
			||||||
 | 
							dev: uint64(stat.Dev), //nolint: unconvert // dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd
 | 
				
			||||||
 | 
							ino: uint64(stat.Ino), //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							size   int64
 | 
				
			||||||
 | 
							inodes = map[inode]struct{}{} // expensive!
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, root := range roots {
 | 
				
			||||||
 | 
							if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								select {
 | 
				
			||||||
 | 
								case <-ctx.Done():
 | 
				
			||||||
 | 
									return ctx.Err()
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stat := fi.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
								inoKey := newInode(stat)
 | 
				
			||||||
 | 
								if _, ok := inodes[inoKey]; !ok {
 | 
				
			||||||
 | 
									inodes[inoKey] = struct{}{}
 | 
				
			||||||
 | 
									size += stat.Blocks * blocksUnitSize
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}); err != nil {
 | 
				
			||||||
 | 
								return Usage{}, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Usage{
 | 
				
			||||||
 | 
							Inodes: int64(len(inodes)),
 | 
				
			||||||
 | 
							Size:   size,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func diffUsage(ctx context.Context, a, b string) (Usage, error) {
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							size   int64
 | 
				
			||||||
 | 
							inodes = map[inode]struct{}{} // expensive!
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if kind == ChangeKindAdd || kind == ChangeKindModify {
 | 
				
			||||||
 | 
								stat := fi.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
								inoKey := newInode(stat)
 | 
				
			||||||
 | 
								if _, ok := inodes[inoKey]; !ok {
 | 
				
			||||||
 | 
									inodes[inoKey] = struct{}{}
 | 
				
			||||||
 | 
									size += stat.Blocks * blocksUnitSize
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							return Usage{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Usage{
 | 
				
			||||||
 | 
							Inodes: int64(len(inodes)),
 | 
				
			||||||
 | 
							Size:   size,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					//go:build windows
 | 
				
			||||||
 | 
					// +build windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							size int64
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO(stevvooe): Support inodes (or equivalent) for windows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, root := range roots {
 | 
				
			||||||
 | 
							if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								select {
 | 
				
			||||||
 | 
								case <-ctx.Done():
 | 
				
			||||||
 | 
									return ctx.Err()
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								size += fi.Size()
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}); err != nil {
 | 
				
			||||||
 | 
								return Usage{}, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Usage{
 | 
				
			||||||
 | 
							Size: size,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func diffUsage(ctx context.Context, a, b string) (Usage, error) {
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							size int64
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if kind == ChangeKindAdd || kind == ChangeKindModify {
 | 
				
			||||||
 | 
								size += fi.Size()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							return Usage{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Usage{
 | 
				
			||||||
 | 
							Size: size,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetLinkInfo returns an identifier representing the node a hardlink is pointing
 | 
				
			||||||
 | 
					// to. If the file is not hard linked then 0 will be returned.
 | 
				
			||||||
 | 
					func GetLinkInfo(fi os.FileInfo) (uint64, bool) {
 | 
				
			||||||
 | 
						return getLinkInfo(fi)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getLinkSource returns a path for the given name and
 | 
				
			||||||
 | 
					// file info to its link source in the provided inode
 | 
				
			||||||
 | 
					// map. If the given file name is not in the map and
 | 
				
			||||||
 | 
					// has other links, it is added to the inode map
 | 
				
			||||||
 | 
					// to be a source for other link locations.
 | 
				
			||||||
 | 
					func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
 | 
				
			||||||
 | 
						inode, isHardlink := getLinkInfo(fi)
 | 
				
			||||||
 | 
						if !isHardlink {
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						path, ok := inodes[inode]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							inodes[inode] = name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return path, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getLinkInfo(fi os.FileInfo) (uint64, bool) {
 | 
				
			||||||
 | 
						s, ok := fi.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return 0, false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getLinkInfo(fi os.FileInfo) (uint64, bool) {
 | 
				
			||||||
 | 
						return 0, false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,310 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						errTooManyLinks = errors.New("too many links")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type currentPath struct {
 | 
				
			||||||
 | 
						path     string
 | 
				
			||||||
 | 
						f        os.FileInfo
 | 
				
			||||||
 | 
						fullPath string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func pathChange(lower, upper *currentPath) (ChangeKind, string) {
 | 
				
			||||||
 | 
						if lower == nil {
 | 
				
			||||||
 | 
							if upper == nil {
 | 
				
			||||||
 | 
								panic("cannot compare nil paths")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ChangeKindAdd, upper.path
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if upper == nil {
 | 
				
			||||||
 | 
							return ChangeKindDelete, lower.path
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch i := directoryCompare(lower.path, upper.path); {
 | 
				
			||||||
 | 
						case i < 0:
 | 
				
			||||||
 | 
							// File in lower that is not in upper
 | 
				
			||||||
 | 
							return ChangeKindDelete, lower.path
 | 
				
			||||||
 | 
						case i > 0:
 | 
				
			||||||
 | 
							// File in upper that is not in lower
 | 
				
			||||||
 | 
							return ChangeKindAdd, upper.path
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return ChangeKindModify, upper.path
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func directoryCompare(a, b string) int {
 | 
				
			||||||
 | 
						l := len(a)
 | 
				
			||||||
 | 
						if len(b) < l {
 | 
				
			||||||
 | 
							l = len(b)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i := 0; i < l; i++ {
 | 
				
			||||||
 | 
							c1, c2 := a[i], b[i]
 | 
				
			||||||
 | 
							if c1 == filepath.Separator {
 | 
				
			||||||
 | 
								c1 = byte(0)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if c2 == filepath.Separator {
 | 
				
			||||||
 | 
								c2 = byte(0)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if c1 < c2 {
 | 
				
			||||||
 | 
								return -1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if c1 > c2 {
 | 
				
			||||||
 | 
								return +1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(a) < len(b) {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(a) > len(b) {
 | 
				
			||||||
 | 
							return +1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func sameFile(f1, f2 *currentPath) (bool, error) {
 | 
				
			||||||
 | 
						if os.SameFile(f1.f, f2.f) {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						equalStat, err := compareSysStat(f1.f.Sys(), f2.f.Sys())
 | 
				
			||||||
 | 
						if err != nil || !equalStat {
 | 
				
			||||||
 | 
							return equalStat, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if eq, err := compareCapabilities(f1.fullPath, f2.fullPath); err != nil || !eq {
 | 
				
			||||||
 | 
							return eq, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If not a directory also check size, modtime, and content
 | 
				
			||||||
 | 
						if !f1.f.IsDir() {
 | 
				
			||||||
 | 
							if f1.f.Size() != f2.f.Size() {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							t1 := f1.f.ModTime()
 | 
				
			||||||
 | 
							t2 := f2.f.ModTime()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if t1.Unix() != t2.Unix() {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// If the timestamp may have been truncated in both of the
 | 
				
			||||||
 | 
							// files, check content of file to determine difference
 | 
				
			||||||
 | 
							if t1.Nanosecond() == 0 && t2.Nanosecond() == 0 {
 | 
				
			||||||
 | 
								if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink {
 | 
				
			||||||
 | 
									return compareSymlinkTarget(f1.fullPath, f2.fullPath)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if f1.f.Size() == 0 { // if file sizes are zero length, the files are the same by definition
 | 
				
			||||||
 | 
									return true, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return compareFileContent(f1.fullPath, f2.fullPath)
 | 
				
			||||||
 | 
							} else if t1.Nanosecond() != t2.Nanosecond() {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func compareSymlinkTarget(p1, p2 string) (bool, error) {
 | 
				
			||||||
 | 
						t1, err := os.Readlink(p1)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t2, err := os.Readlink(p2)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return t1 == t2, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const compareChuckSize = 32 * 1024
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// compareFileContent compares the content of 2 same sized files
 | 
				
			||||||
 | 
					// by comparing each byte.
 | 
				
			||||||
 | 
					func compareFileContent(p1, p2 string) (bool, error) {
 | 
				
			||||||
 | 
						f1, err := os.Open(p1)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f1.Close()
 | 
				
			||||||
 | 
						f2, err := os.Open(p2)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f2.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b1 := make([]byte, compareChuckSize)
 | 
				
			||||||
 | 
						b2 := make([]byte, compareChuckSize)
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							n1, err1 := f1.Read(b1)
 | 
				
			||||||
 | 
							if err1 != nil && err1 != io.EOF {
 | 
				
			||||||
 | 
								return false, err1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							n2, err2 := f2.Read(b2)
 | 
				
			||||||
 | 
							if err2 != nil && err2 != io.EOF {
 | 
				
			||||||
 | 
								return false, err2
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if n1 != n2 || !bytes.Equal(b1[:n1], b2[:n2]) {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err1 == io.EOF && err2 == io.EOF {
 | 
				
			||||||
 | 
								return true, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func pathWalk(ctx context.Context, root string, pathC chan<- *currentPath) error {
 | 
				
			||||||
 | 
						return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Rebase path
 | 
				
			||||||
 | 
							path, err = filepath.Rel(root, path)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							path = filepath.Join(string(os.PathSeparator), path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Skip root
 | 
				
			||||||
 | 
							if path == string(os.PathSeparator) {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							p := ¤tPath{
 | 
				
			||||||
 | 
								path:     path,
 | 
				
			||||||
 | 
								f:        f,
 | 
				
			||||||
 | 
								fullPath: filepath.Join(root, path),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-ctx.Done():
 | 
				
			||||||
 | 
								return ctx.Err()
 | 
				
			||||||
 | 
							case pathC <- p:
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func nextPath(ctx context.Context, pathC <-chan *currentPath) (*currentPath, error) {
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case <-ctx.Done():
 | 
				
			||||||
 | 
							return nil, ctx.Err()
 | 
				
			||||||
 | 
						case p := <-pathC:
 | 
				
			||||||
 | 
							return p, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RootPath joins a path with a root, evaluating and bounding any
 | 
				
			||||||
 | 
					// symlink to the root directory.
 | 
				
			||||||
 | 
					func RootPath(root, path string) (string, error) {
 | 
				
			||||||
 | 
						if path == "" {
 | 
				
			||||||
 | 
							return root, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var linksWalked int // to protect against cycles
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							i := linksWalked
 | 
				
			||||||
 | 
							newpath, err := walkLinks(root, path, &linksWalked)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							path = newpath
 | 
				
			||||||
 | 
							if i == linksWalked {
 | 
				
			||||||
 | 
								newpath = filepath.Join("/", newpath)
 | 
				
			||||||
 | 
								if path == newpath {
 | 
				
			||||||
 | 
									return filepath.Join(root, newpath), nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								path = newpath
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func walkLink(root, path string, linksWalked *int) (newpath string, islink bool, err error) {
 | 
				
			||||||
 | 
						if *linksWalked > 255 {
 | 
				
			||||||
 | 
							return "", false, errTooManyLinks
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						path = filepath.Join("/", path)
 | 
				
			||||||
 | 
						if path == "/" {
 | 
				
			||||||
 | 
							return path, false, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						realPath := filepath.Join(root, path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fi, err := os.Lstat(realPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// If path does not yet exist, treat as non-symlink
 | 
				
			||||||
 | 
							if os.IsNotExist(err) {
 | 
				
			||||||
 | 
								return path, false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "", false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if fi.Mode()&os.ModeSymlink == 0 {
 | 
				
			||||||
 | 
							return path, false, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						newpath, err = os.Readlink(realPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*linksWalked++
 | 
				
			||||||
 | 
						return newpath, true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func walkLinks(root, path string, linksWalked *int) (string, error) {
 | 
				
			||||||
 | 
						switch dir, file := filepath.Split(path); {
 | 
				
			||||||
 | 
						case dir == "":
 | 
				
			||||||
 | 
							newpath, _, err := walkLink(root, file, linksWalked)
 | 
				
			||||||
 | 
							return newpath, err
 | 
				
			||||||
 | 
						case file == "":
 | 
				
			||||||
 | 
							if os.IsPathSeparator(dir[len(dir)-1]) {
 | 
				
			||||||
 | 
								if dir == "/" {
 | 
				
			||||||
 | 
									return dir, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return walkLinks(root, dir[:len(dir)-1], linksWalked)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							newpath, _, err := walkLink(root, dir, linksWalked)
 | 
				
			||||||
 | 
							return newpath, err
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							newdir, err := walkLinks(root, dir, linksWalked)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							newpath, islink, err := walkLink(root, filepath.Join(newdir, file), linksWalked)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !islink {
 | 
				
			||||||
 | 
								return newpath, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if filepath.IsAbs(newpath) {
 | 
				
			||||||
 | 
								return newpath, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return filepath.Join(newdir, newpath), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					//go:build linux || openbsd || solaris
 | 
				
			||||||
 | 
					// +build linux openbsd solaris
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatAtime returns the Atim
 | 
				
			||||||
 | 
					func StatAtime(st *syscall.Stat_t) syscall.Timespec {
 | 
				
			||||||
 | 
						return st.Atim
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatCtime returns the Ctim
 | 
				
			||||||
 | 
					func StatCtime(st *syscall.Stat_t) syscall.Timespec {
 | 
				
			||||||
 | 
						return st.Ctim
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatMtime returns the Mtim
 | 
				
			||||||
 | 
					func StatMtime(st *syscall.Stat_t) syscall.Timespec {
 | 
				
			||||||
 | 
						return st.Mtim
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatATimeAsTime returns st.Atim as a time.Time
 | 
				
			||||||
 | 
					func StatATimeAsTime(st *syscall.Stat_t) time.Time {
 | 
				
			||||||
 | 
						return time.Unix(st.Atim.Unix())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					//go:build darwin || freebsd || netbsd
 | 
				
			||||||
 | 
					// +build darwin freebsd netbsd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatAtime returns the access time from a stat struct
 | 
				
			||||||
 | 
					func StatAtime(st *syscall.Stat_t) syscall.Timespec {
 | 
				
			||||||
 | 
						return st.Atimespec
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatCtime returns the created time from a stat struct
 | 
				
			||||||
 | 
					func StatCtime(st *syscall.Stat_t) syscall.Timespec {
 | 
				
			||||||
 | 
						return st.Ctimespec
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatMtime returns the modified time from a stat struct
 | 
				
			||||||
 | 
					func StatMtime(st *syscall.Stat_t) syscall.Timespec {
 | 
				
			||||||
 | 
						return st.Mtimespec
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatATimeAsTime returns the access time as a time.Time
 | 
				
			||||||
 | 
					func StatATimeAsTime(st *syscall.Stat_t) time.Time {
 | 
				
			||||||
 | 
						return time.Unix(st.Atimespec.Unix())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Gnu tar and the go tar writer don't have sub-second mtime
 | 
				
			||||||
 | 
					// precision, which is problematic when we apply changes via tar
 | 
				
			||||||
 | 
					// files, we handle this by comparing for exact times, *or* same
 | 
				
			||||||
 | 
					// second count and either a or b having exactly 0 nanoseconds
 | 
				
			||||||
 | 
					func sameFsTime(a, b time.Time) bool {
 | 
				
			||||||
 | 
						return a == b ||
 | 
				
			||||||
 | 
							(a.Unix() == b.Unix() &&
 | 
				
			||||||
 | 
								(a.Nanosecond() == 0 || b.Nanosecond() == 0))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					//go:build !(windows || linux)
 | 
				
			||||||
 | 
					// +build !windows,!linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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 fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/sys/unix"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func utimesNano(name string, atime, mtime syscall.Timespec) error {
 | 
				
			||||||
 | 
						at := unix.NsecToTimespec(atime.Nano())
 | 
				
			||||||
 | 
						mt := unix.NsecToTimespec(mtime.Nano())
 | 
				
			||||||
 | 
						utimes := [2]unix.Timespec{at, mt}
 | 
				
			||||||
 | 
						return unix.UtimesNanoAt(unix.AT_FDCWD, name, utimes[0:], unix.AT_SYMLINK_NOFOLLOW)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -224,3 +224,8 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil, errPluginNotFound(name)
 | 
						return nil, errPluginNotFound(name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsPluginCommand checks if the given cmd is a plugin-stub.
 | 
				
			||||||
 | 
					func IsPluginCommand(cmd *cobra.Command) bool {
 | 
				
			||||||
 | 
						return cmd.Annotations[CommandAnnotationPlugin] == "true"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,9 +10,7 @@ import (
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var pluginNameRe = regexp.MustCompile("^[a-z][a-z0-9]*$")
 | 
				
			||||||
	pluginNameRe = regexp.MustCompile("^[a-z][a-z0-9]*$")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Plugin represents a potential plugin with all it's metadata.
 | 
					// Plugin represents a potential plugin with all it's metadata.
 | 
				
			||||||
type Plugin struct {
 | 
					type Plugin struct {
 | 
				
			||||||
| 
						 | 
					@ -33,8 +31,6 @@ type Plugin struct {
 | 
				
			||||||
// is set, and is always a `pluginError`, but the `Plugin` is still
 | 
					// is set, and is always a `pluginError`, but the `Plugin` is still
 | 
				
			||||||
// returned with no error. An error is only returned due to a
 | 
					// returned with no error. An error is only returned due to a
 | 
				
			||||||
// non-recoverable error.
 | 
					// non-recoverable error.
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//nolint:gocyclo
 | 
					 | 
				
			||||||
func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
 | 
					func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
 | 
				
			||||||
	path := c.Path()
 | 
						path := c.Path()
 | 
				
			||||||
	if path == "" {
 | 
						if path == "" {
 | 
				
			||||||
| 
						 | 
					@ -71,7 +67,7 @@ func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
 | 
				
			||||||
			// Ignore conflicts with commands which are
 | 
								// Ignore conflicts with commands which are
 | 
				
			||||||
			// just plugin stubs (i.e. from a previous
 | 
								// just plugin stubs (i.e. from a previous
 | 
				
			||||||
			// call to AddPluginCommandStubs).
 | 
								// call to AddPluginCommandStubs).
 | 
				
			||||||
			if p := cmd.Annotations[CommandAnnotationPlugin]; p == "true" {
 | 
								if IsPluginCommand(cmd) {
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if cmd.Name() == p.Name {
 | 
								if cmd.Name() == p.Name {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package manager
 | 
				
			||||||
func trimExeSuffix(s string) (string, error) {
 | 
					func trimExeSuffix(s string) (string, error) {
 | 
				
			||||||
	return s, nil
 | 
						return s, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func addExeSuffix(s string) string {
 | 
					func addExeSuffix(s string) string {
 | 
				
			||||||
	return s
 | 
						return s
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p
 | 
				
			||||||
	flags := rootCmd.Flags()
 | 
						flags := rootCmd.Flags()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flags.StringVar(&opts.ConfigDir, "config", config.Dir(), "Location of client config files")
 | 
						flags.StringVar(&opts.ConfigDir, "config", config.Dir(), "Location of client config files")
 | 
				
			||||||
	opts.Common.InstallFlags(flags)
 | 
						opts.InstallFlags(flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cobra.AddTemplateFunc("add", func(a, b int) int { return a + b })
 | 
						cobra.AddTemplateFunc("add", func(a, b int) int { return a + b })
 | 
				
			||||||
	cobra.AddTemplateFunc("hasAliases", hasAliases)
 | 
						cobra.AddTemplateFunc("hasAliases", hasAliases)
 | 
				
			||||||
| 
						 | 
					@ -172,7 +172,7 @@ func (tcmd *TopLevelCommand) HandleGlobalFlags() (*cobra.Command, []string, erro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Initialize finalises global option parsing and initializes the docker client.
 | 
					// Initialize finalises global option parsing and initializes the docker client.
 | 
				
			||||||
func (tcmd *TopLevelCommand) Initialize(ops ...command.InitializeOpt) error {
 | 
					func (tcmd *TopLevelCommand) Initialize(ops ...command.InitializeOpt) error {
 | 
				
			||||||
	tcmd.opts.Common.SetDefaultOptions(tcmd.flags)
 | 
						tcmd.opts.SetDefaultOptions(tcmd.flags)
 | 
				
			||||||
	return tcmd.dockerCli.Initialize(tcmd.opts, ops...)
 | 
						return tcmd.dockerCli.Initialize(tcmd.opts, ops...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -237,7 +237,7 @@ func hasAdditionalHelp(cmd *cobra.Command) bool {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isPlugin(cmd *cobra.Command) bool {
 | 
					func isPlugin(cmd *cobra.Command) bool {
 | 
				
			||||||
	return cmd.Annotations[pluginmanager.CommandAnnotationPlugin] == "true"
 | 
						return pluginmanager.IsPluginCommand(cmd)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func hasAliases(cmd *cobra.Command) bool {
 | 
					func hasAliases(cmd *cobra.Command) bool {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,6 @@ import (
 | 
				
			||||||
	"github.com/docker/docker/api/types/swarm"
 | 
						"github.com/docker/docker/api/types/swarm"
 | 
				
			||||||
	"github.com/docker/docker/client"
 | 
						"github.com/docker/docker/client"
 | 
				
			||||||
	"github.com/docker/go-connections/tlsconfig"
 | 
						"github.com/docker/go-connections/tlsconfig"
 | 
				
			||||||
	"github.com/moby/term"
 | 
					 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
	notaryclient "github.com/theupdateframework/notary/client"
 | 
						notaryclient "github.com/theupdateframework/notary/client"
 | 
				
			||||||
| 
						 | 
					@ -203,15 +202,18 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cliflags.SetLogLevel(opts.Common.LogLevel)
 | 
						cliflags.SetLogLevel(opts.LogLevel)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if opts.ConfigDir != "" {
 | 
						if opts.ConfigDir != "" {
 | 
				
			||||||
		config.SetDir(opts.ConfigDir)
 | 
							config.SetDir(opts.ConfigDir)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if opts.Common.Debug {
 | 
						if opts.Debug {
 | 
				
			||||||
		debug.Enable()
 | 
							debug.Enable()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if opts.Context != "" && len(opts.Hosts) > 0 {
 | 
				
			||||||
 | 
							return errors.New("conflicting options: either specify --host or --context, not both")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cli.loadConfigFile()
 | 
						cli.loadConfigFile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,13 +221,10 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
 | 
				
			||||||
	cli.contextStore = &ContextStoreWithDefault{
 | 
						cli.contextStore = &ContextStoreWithDefault{
 | 
				
			||||||
		Store: baseContextStore,
 | 
							Store: baseContextStore,
 | 
				
			||||||
		Resolver: func() (*DefaultContext, error) {
 | 
							Resolver: func() (*DefaultContext, error) {
 | 
				
			||||||
			return ResolveDefaultContext(opts.Common, cli.contextStoreConfig)
 | 
								return ResolveDefaultContext(opts, cli.contextStoreConfig)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore)
 | 
						cli.currentContext = resolveContextName(opts, cli.configFile)
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext)
 | 
						cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.Wrap(err, "unable to resolve docker endpoint")
 | 
							return errors.Wrap(err, "unable to resolve docker endpoint")
 | 
				
			||||||
| 
						 | 
					@ -242,7 +241,11 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewAPIClientFromFlags creates a new APIClient from command line flags
 | 
					// NewAPIClientFromFlags creates a new APIClient from command line flags
 | 
				
			||||||
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
 | 
					func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
 | 
				
			||||||
 | 
						if opts.Context != "" && len(opts.Hosts) > 0 {
 | 
				
			||||||
 | 
							return nil, errors.New("conflicting options: either specify --host or --context, not both")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	storeConfig := DefaultContextStoreConfig()
 | 
						storeConfig := DefaultContextStoreConfig()
 | 
				
			||||||
	contextStore := &ContextStoreWithDefault{
 | 
						contextStore := &ContextStoreWithDefault{
 | 
				
			||||||
		Store: store.New(config.ContextStoreDir(), storeConfig),
 | 
							Store: store.New(config.ContextStoreDir(), storeConfig),
 | 
				
			||||||
| 
						 | 
					@ -250,11 +253,7 @@ func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.
 | 
				
			||||||
			return ResolveDefaultContext(opts, storeConfig)
 | 
								return ResolveDefaultContext(opts, storeConfig)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	contextName, err := resolveContextName(opts, configFile, contextStore)
 | 
						endpoint, err := resolveDockerEndpoint(contextStore, resolveContextName(opts, configFile))
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	endpoint, err := resolveDockerEndpoint(contextStore, contextName)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrap(err, "unable to resolve docker endpoint")
 | 
							return nil, errors.Wrap(err, "unable to resolve docker endpoint")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -288,7 +287,7 @@ func resolveDockerEndpoint(s store.Reader, contextName string) (docker.Endpoint,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Resolve the Docker endpoint for the default context (based on config, env vars and CLI flags)
 | 
					// Resolve the Docker endpoint for the default context (based on config, env vars and CLI flags)
 | 
				
			||||||
func resolveDefaultDockerEndpoint(opts *cliflags.CommonOptions) (docker.Endpoint, error) {
 | 
					func resolveDefaultDockerEndpoint(opts *cliflags.ClientOptions) (docker.Endpoint, error) {
 | 
				
			||||||
	host, err := getServerHost(opts.Hosts, opts.TLSOptions)
 | 
						host, err := getServerHost(opts.Hosts, opts.TLSOptions)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return docker.Endpoint{}, err
 | 
							return docker.Endpoint{}, err
 | 
				
			||||||
| 
						 | 
					@ -363,11 +362,63 @@ func (cli *DockerCli) ContextStore() store.Store {
 | 
				
			||||||
	return cli.contextStore
 | 
						return cli.contextStore
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CurrentContext returns the current context name
 | 
					// CurrentContext returns the current context name, based on flags,
 | 
				
			||||||
 | 
					// environment variables and the cli configuration file, in the following
 | 
				
			||||||
 | 
					// order of preference:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  1. The "--context" command-line option.
 | 
				
			||||||
 | 
					//  2. The "DOCKER_CONTEXT" environment variable.
 | 
				
			||||||
 | 
					//  3. The current context as configured through the in "currentContext"
 | 
				
			||||||
 | 
					//     field in the CLI configuration file ("~/.docker/config.json").
 | 
				
			||||||
 | 
					//  4. If no context is configured, use the "default" context.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// # Fallbacks for backward-compatibility
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// To preserve backward-compatibility with the "pre-contexts" behavior,
 | 
				
			||||||
 | 
					// the "default" context is used if:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//   - The "--host" option is set
 | 
				
			||||||
 | 
					//   - The "DOCKER_HOST" ([DefaultContextName]) environment variable is set
 | 
				
			||||||
 | 
					//     to a non-empty value.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In these cases, the default context is used, which uses the host as
 | 
				
			||||||
 | 
					// specified in "DOCKER_HOST", and TLS config from flags/env vars.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Setting both the "--context" and "--host" flags is ambiguous and results
 | 
				
			||||||
 | 
					// in an error when the cli is started.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// CurrentContext does not validate if the given context exists or if it's
 | 
				
			||||||
 | 
					// valid; errors may occur when trying to use it.
 | 
				
			||||||
func (cli *DockerCli) CurrentContext() string {
 | 
					func (cli *DockerCli) CurrentContext() string {
 | 
				
			||||||
	return cli.currentContext
 | 
						return cli.currentContext
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CurrentContext returns the current context name, based on flags,
 | 
				
			||||||
 | 
					// environment variables and the cli configuration file. It does not
 | 
				
			||||||
 | 
					// validate if the given context exists or if it's valid; errors may
 | 
				
			||||||
 | 
					// occur when trying to use it.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Refer to [DockerCli.CurrentContext] above for further details.
 | 
				
			||||||
 | 
					func resolveContextName(opts *cliflags.ClientOptions, config *configfile.ConfigFile) string {
 | 
				
			||||||
 | 
						if opts != nil && opts.Context != "" {
 | 
				
			||||||
 | 
							return opts.Context
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts != nil && len(opts.Hosts) > 0 {
 | 
				
			||||||
 | 
							return DefaultContextName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if os.Getenv(client.EnvOverrideHost) != "" {
 | 
				
			||||||
 | 
							return DefaultContextName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ctxName := os.Getenv("DOCKER_CONTEXT"); ctxName != "" {
 | 
				
			||||||
 | 
							return ctxName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if config != nil && config.CurrentContext != "" {
 | 
				
			||||||
 | 
							// We don't validate if this context exists: errors may occur when trying to use it.
 | 
				
			||||||
 | 
							return config.CurrentContext
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return DefaultContextName
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DockerEndpoint returns the current docker endpoint
 | 
					// DockerEndpoint returns the current docker endpoint
 | 
				
			||||||
func (cli *DockerCli) DockerEndpoint() docker.Endpoint {
 | 
					func (cli *DockerCli) DockerEndpoint() docker.Endpoint {
 | 
				
			||||||
	return cli.dockerEndpoint
 | 
						return cli.dockerEndpoint
 | 
				
			||||||
| 
						 | 
					@ -407,6 +458,7 @@ func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) {
 | 
				
			||||||
	defaultOps := []DockerCliOption{
 | 
						defaultOps := []DockerCliOption{
 | 
				
			||||||
		WithContentTrustFromEnv(),
 | 
							WithContentTrustFromEnv(),
 | 
				
			||||||
		WithDefaultContextStoreConfig(),
 | 
							WithDefaultContextStoreConfig(),
 | 
				
			||||||
 | 
							WithStandardStreams(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ops = append(defaultOps, ops...)
 | 
						ops = append(defaultOps, ops...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -414,18 +466,6 @@ func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) {
 | 
				
			||||||
	if err := cli.Apply(ops...); err != nil {
 | 
						if err := cli.Apply(ops...); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if cli.out == nil || cli.in == nil || cli.err == nil {
 | 
					 | 
				
			||||||
		stdin, stdout, stderr := term.StdStreams()
 | 
					 | 
				
			||||||
		if cli.in == nil {
 | 
					 | 
				
			||||||
			cli.in = streams.NewIn(stdin)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if cli.out == nil {
 | 
					 | 
				
			||||||
			cli.out = streams.NewOut(stdout)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if cli.err == nil {
 | 
					 | 
				
			||||||
			cli.err = stderr
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return cli, nil
 | 
						return cli, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -448,40 +488,6 @@ func UserAgent() string {
 | 
				
			||||||
	return "Docker-Client/" + version.Version + " (" + runtime.GOOS + ")"
 | 
						return "Docker-Client/" + version.Version + " (" + runtime.GOOS + ")"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// resolveContextName resolves the current context name with the following rules:
 | 
					 | 
				
			||||||
// - setting both --context and --host flags is ambiguous
 | 
					 | 
				
			||||||
// - if --context is set, use this value
 | 
					 | 
				
			||||||
// - if --host flag or DOCKER_HOST (client.EnvOverrideHost) is set, fallbacks to use the same logic as before context-store was added
 | 
					 | 
				
			||||||
// for backward compatibility with existing scripts
 | 
					 | 
				
			||||||
// - if DOCKER_CONTEXT is set, use this value
 | 
					 | 
				
			||||||
// - if Config file has a globally set "CurrentContext", use this value
 | 
					 | 
				
			||||||
// - fallbacks to default HOST, uses TLS config from flags/env vars
 | 
					 | 
				
			||||||
func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigFile, contextstore store.Reader) (string, error) {
 | 
					 | 
				
			||||||
	if opts.Context != "" && len(opts.Hosts) > 0 {
 | 
					 | 
				
			||||||
		return "", errors.New("Conflicting options: either specify --host or --context, not both")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opts.Context != "" {
 | 
					 | 
				
			||||||
		return opts.Context, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(opts.Hosts) > 0 {
 | 
					 | 
				
			||||||
		return DefaultContextName, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if os.Getenv(client.EnvOverrideHost) != "" {
 | 
					 | 
				
			||||||
		return DefaultContextName, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if ctxName := os.Getenv("DOCKER_CONTEXT"); ctxName != "" {
 | 
					 | 
				
			||||||
		return ctxName, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if config != nil && config.CurrentContext != "" {
 | 
					 | 
				
			||||||
		_, err := contextstore.GetMetadata(config.CurrentContext)
 | 
					 | 
				
			||||||
		if store.IsErrContextDoesNotExist(err) {
 | 
					 | 
				
			||||||
			return "", errors.Errorf("Current context %q is not found on the file system, please check your config file at %s", config.CurrentContext, config.Filename)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return config.CurrentContext, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return DefaultContextName, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var defaultStoreEndpoints = []store.NamedTypeGetter{
 | 
					var defaultStoreEndpoints = []store.NamedTypeGetter{
 | 
				
			||||||
	store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
 | 
						store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,5 +58,8 @@ func GetDockerContext(storeMetadata store.Metadata) (DockerContext, error) {
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return DockerContext{}, errors.New("context metadata is not a valid DockerContext")
 | 
							return DockerContext{}, errors.New("context metadata is not a valid DockerContext")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if storeMetadata.Name == DefaultContextName {
 | 
				
			||||||
 | 
							res.Description = "Current DOCKER_HOST based configuration"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return res, nil
 | 
						return res, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
package command
 | 
					package command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/docker/cli/cli/context/docker"
 | 
						"github.com/docker/cli/cli/context/docker"
 | 
				
			||||||
	"github.com/docker/cli/cli/context/store"
 | 
						"github.com/docker/cli/cli/context/store"
 | 
				
			||||||
	cliflags "github.com/docker/cli/cli/flags"
 | 
						cliflags "github.com/docker/cli/cli/flags"
 | 
				
			||||||
 | 
						"github.com/docker/docker/errdefs"
 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,7 +42,7 @@ type EndpointDefaultResolver interface {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ResolveDefaultContext creates a Metadata for the current CLI invocation parameters
 | 
					// ResolveDefaultContext creates a Metadata for the current CLI invocation parameters
 | 
				
			||||||
func ResolveDefaultContext(opts *cliflags.CommonOptions, config store.Config) (*DefaultContext, error) {
 | 
					func ResolveDefaultContext(opts *cliflags.ClientOptions, config store.Config) (*DefaultContext, error) {
 | 
				
			||||||
	contextTLSData := store.ContextTLSData{
 | 
						contextTLSData := store.ContextTLSData{
 | 
				
			||||||
		Endpoints: make(map[string]store.EndpointTLSData),
 | 
							Endpoints: make(map[string]store.EndpointTLSData),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -107,7 +106,7 @@ func (s *ContextStoreWithDefault) List() ([]store.Metadata, error) {
 | 
				
			||||||
// CreateOrUpdate is not allowed for the default context and fails
 | 
					// CreateOrUpdate is not allowed for the default context and fails
 | 
				
			||||||
func (s *ContextStoreWithDefault) CreateOrUpdate(meta store.Metadata) error {
 | 
					func (s *ContextStoreWithDefault) CreateOrUpdate(meta store.Metadata) error {
 | 
				
			||||||
	if meta.Name == DefaultContextName {
 | 
						if meta.Name == DefaultContextName {
 | 
				
			||||||
		return errors.New("default context cannot be created nor updated")
 | 
							return errdefs.InvalidParameter(errors.New("default context cannot be created nor updated"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return s.Store.CreateOrUpdate(meta)
 | 
						return s.Store.CreateOrUpdate(meta)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -115,7 +114,7 @@ func (s *ContextStoreWithDefault) CreateOrUpdate(meta store.Metadata) error {
 | 
				
			||||||
// Remove is not allowed for the default context and fails
 | 
					// Remove is not allowed for the default context and fails
 | 
				
			||||||
func (s *ContextStoreWithDefault) Remove(name string) error {
 | 
					func (s *ContextStoreWithDefault) Remove(name string) error {
 | 
				
			||||||
	if name == DefaultContextName {
 | 
						if name == DefaultContextName {
 | 
				
			||||||
		return errors.New("default context cannot be removed")
 | 
							return errdefs.InvalidParameter(errors.New("default context cannot be removed"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return s.Store.Remove(name)
 | 
						return s.Store.Remove(name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -135,7 +134,7 @@ func (s *ContextStoreWithDefault) GetMetadata(name string) (store.Metadata, erro
 | 
				
			||||||
// ResetTLSMaterial is not implemented for default context and fails
 | 
					// ResetTLSMaterial is not implemented for default context and fails
 | 
				
			||||||
func (s *ContextStoreWithDefault) ResetTLSMaterial(name string, data *store.ContextTLSData) error {
 | 
					func (s *ContextStoreWithDefault) ResetTLSMaterial(name string, data *store.ContextTLSData) error {
 | 
				
			||||||
	if name == DefaultContextName {
 | 
						if name == DefaultContextName {
 | 
				
			||||||
		return errors.New("The default context store does not support ResetTLSMaterial")
 | 
							return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return s.Store.ResetTLSMaterial(name, data)
 | 
						return s.Store.ResetTLSMaterial(name, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -143,7 +142,7 @@ func (s *ContextStoreWithDefault) ResetTLSMaterial(name string, data *store.Cont
 | 
				
			||||||
// ResetEndpointTLSMaterial is not implemented for default context and fails
 | 
					// ResetEndpointTLSMaterial is not implemented for default context and fails
 | 
				
			||||||
func (s *ContextStoreWithDefault) ResetEndpointTLSMaterial(contextName string, endpointName string, data *store.EndpointTLSData) error {
 | 
					func (s *ContextStoreWithDefault) ResetEndpointTLSMaterial(contextName string, endpointName string, data *store.EndpointTLSData) error {
 | 
				
			||||||
	if contextName == DefaultContextName {
 | 
						if contextName == DefaultContextName {
 | 
				
			||||||
		return errors.New("The default context store does not support ResetEndpointTLSMaterial")
 | 
							return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return s.Store.ResetEndpointTLSMaterial(contextName, endpointName, data)
 | 
						return s.Store.ResetEndpointTLSMaterial(contextName, endpointName, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -176,29 +175,13 @@ func (s *ContextStoreWithDefault) GetTLSData(contextName, endpointName, fileName
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if defaultContext.TLS.Endpoints[endpointName].Files[fileName] == nil {
 | 
							if defaultContext.TLS.Endpoints[endpointName].Files[fileName] == nil {
 | 
				
			||||||
			return nil, &noDefaultTLSDataError{endpointName: endpointName, fileName: fileName}
 | 
								return nil, errdefs.NotFound(errors.Errorf("TLS data for %s/%s/%s does not exist", DefaultContextName, endpointName, fileName))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return defaultContext.TLS.Endpoints[endpointName].Files[fileName], nil
 | 
							return defaultContext.TLS.Endpoints[endpointName].Files[fileName], nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return s.Store.GetTLSData(contextName, endpointName, fileName)
 | 
						return s.Store.GetTLSData(contextName, endpointName, fileName)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type noDefaultTLSDataError struct {
 | 
					 | 
				
			||||||
	endpointName string
 | 
					 | 
				
			||||||
	fileName     string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (e *noDefaultTLSDataError) Error() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("tls data for %s/%s/%s does not exist", DefaultContextName, e.endpointName, e.fileName)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
 | 
					 | 
				
			||||||
func (e *noDefaultTLSDataError) NotFound() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsTLSDataDoesNotExist satisfies github.com/docker/cli/cli/context/store.tlsDataDoesNotExist
 | 
					 | 
				
			||||||
func (e *noDefaultTLSDataError) IsTLSDataDoesNotExist() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetStorageInfo implements store.Store's GetStorageInfo
 | 
					// GetStorageInfo implements store.Store's GetStorageInfo
 | 
				
			||||||
func (s *ContextStoreWithDefault) GetStorageInfo(contextName string) store.StorageInfo {
 | 
					func (s *ContextStoreWithDefault) GetStorageInfo(contextName string) store.StorageInfo {
 | 
				
			||||||
	if contextName == DefaultContextName {
 | 
						if contextName == DefaultContextName {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,7 +74,7 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is
 | 
				
			||||||
	if !isDefaultRegistry {
 | 
						if !isDefaultRegistry {
 | 
				
			||||||
		serverAddress = registry.ConvertToHostname(serverAddress)
 | 
							serverAddress = registry.ConvertToHostname(serverAddress)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var authconfig = configtypes.AuthConfig{}
 | 
						authconfig := configtypes.AuthConfig{}
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if checkCredStore {
 | 
						if checkCredStore {
 | 
				
			||||||
		authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress)
 | 
							authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/docker/cli/cli/streams"
 | 
						"github.com/docker/cli/cli/streams"
 | 
				
			||||||
	"github.com/docker/docker/api/types/filters"
 | 
						"github.com/docker/docker/api/types/filters"
 | 
				
			||||||
	"github.com/docker/docker/pkg/system"
 | 
						"github.com/moby/sys/sequential"
 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"github.com/spf13/pflag"
 | 
						"github.com/spf13/pflag"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ import (
 | 
				
			||||||
func CopyToFile(outfile string, r io.Reader) error {
 | 
					func CopyToFile(outfile string, r io.Reader) error {
 | 
				
			||||||
	// We use sequential file access here to avoid depleting the standby list
 | 
						// We use sequential file access here to avoid depleting the standby list
 | 
				
			||||||
	// on Windows. On Linux, this is a call directly to os.CreateTemp
 | 
						// on Windows. On Linux, this is a call directly to os.CreateTemp
 | 
				
			||||||
	tmpFile, err := system.TempFileSequential(filepath.Dir(outfile), ".docker_temp_")
 | 
						tmpFile, err := sequential.CreateTemp(filepath.Dir(outfile), ".docker_temp_")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,7 +139,7 @@ func (configFile *ConfigFile) Save() (retErr error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dir := filepath.Dir(configFile.Filename)
 | 
						dir := filepath.Dir(configFile.Filename)
 | 
				
			||||||
	if err := os.MkdirAll(dir, 0700); err != nil {
 | 
						if err := os.MkdirAll(dir, 0o700); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	temp, err := os.CreateTemp(dir, filepath.Base(configFile.Filename))
 | 
						temp, err := os.CreateTemp(dir, filepath.Base(configFile.Filename))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ import (
 | 
				
			||||||
// ignoring any error during the process.
 | 
					// ignoring any error during the process.
 | 
				
			||||||
func copyFilePermissions(src, dst string) {
 | 
					func copyFilePermissions(src, dst string) {
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		mode     os.FileMode = 0600
 | 
							mode     os.FileMode = 0o600
 | 
				
			||||||
		uid, gid int
 | 
							uid, gid int
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -236,17 +236,21 @@ func (c *commandConn) Close() error {
 | 
				
			||||||
func (c *commandConn) LocalAddr() net.Addr {
 | 
					func (c *commandConn) LocalAddr() net.Addr {
 | 
				
			||||||
	return c.localAddr
 | 
						return c.localAddr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *commandConn) RemoteAddr() net.Addr {
 | 
					func (c *commandConn) RemoteAddr() net.Addr {
 | 
				
			||||||
	return c.remoteAddr
 | 
						return c.remoteAddr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *commandConn) SetDeadline(t time.Time) error {
 | 
					func (c *commandConn) SetDeadline(t time.Time) error {
 | 
				
			||||||
	logrus.Debugf("unimplemented call: SetDeadline(%v)", t)
 | 
						logrus.Debugf("unimplemented call: SetDeadline(%v)", t)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *commandConn) SetReadDeadline(t time.Time) error {
 | 
					func (c *commandConn) SetReadDeadline(t time.Time) error {
 | 
				
			||||||
	logrus.Debugf("unimplemented call: SetReadDeadline(%v)", t)
 | 
						logrus.Debugf("unimplemented call: SetReadDeadline(%v)", t)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *commandConn) SetWriteDeadline(t time.Time) error {
 | 
					func (c *commandConn) SetWriteDeadline(t time.Time) error {
 | 
				
			||||||
	logrus.Debugf("unimplemented call: SetWriteDeadline(%v)", t)
 | 
						logrus.Debugf("unimplemented call: SetWriteDeadline(%v)", t)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,13 +2,14 @@ package store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/errdefs"
 | 
				
			||||||
	"github.com/fvbommel/sortorder"
 | 
						"github.com/fvbommel/sortorder"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
| 
						 | 
					@ -27,14 +28,14 @@ func (s *metadataStore) contextDir(id contextdir) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *metadataStore) createOrUpdate(meta Metadata) error {
 | 
					func (s *metadataStore) createOrUpdate(meta Metadata) error {
 | 
				
			||||||
	contextDir := s.contextDir(contextdirOf(meta.Name))
 | 
						contextDir := s.contextDir(contextdirOf(meta.Name))
 | 
				
			||||||
	if err := os.MkdirAll(contextDir, 0755); err != nil {
 | 
						if err := os.MkdirAll(contextDir, 0o755); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	bytes, err := json.Marshal(&meta)
 | 
						bytes, err := json.Marshal(&meta)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return os.WriteFile(filepath.Join(contextDir, metaFile), bytes, 0644)
 | 
						return os.WriteFile(filepath.Join(contextDir, metaFile), bytes, 0o644)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
 | 
					func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
 | 
				
			||||||
| 
						 | 
					@ -55,11 +56,21 @@ func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
 | 
				
			||||||
	return reflect.ValueOf(typed).Elem().Interface(), nil
 | 
						return reflect.ValueOf(typed).Elem().Interface(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *metadataStore) get(id contextdir) (Metadata, error) {
 | 
					func (s *metadataStore) get(name string) (Metadata, error) {
 | 
				
			||||||
	contextDir := s.contextDir(id)
 | 
						m, err := s.getByID(contextdirOf(name))
 | 
				
			||||||
	bytes, err := os.ReadFile(filepath.Join(contextDir, metaFile))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return Metadata{}, convertContextDoesNotExist(err)
 | 
							return m, errors.Wrapf(err, "load context %q", name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return m, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
 | 
				
			||||||
 | 
						bytes, err := os.ReadFile(filepath.Join(s.contextDir(id), metaFile))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, os.ErrNotExist) {
 | 
				
			||||||
 | 
								return Metadata{}, errdefs.NotFound(errors.Wrap(err, "context does not exist"))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return Metadata{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var untyped untypedContextMetadata
 | 
						var untyped untypedContextMetadata
 | 
				
			||||||
	r := Metadata{
 | 
						r := Metadata{
 | 
				
			||||||
| 
						 | 
					@ -80,24 +91,29 @@ func (s *metadataStore) get(id contextdir) (Metadata, error) {
 | 
				
			||||||
	return r, err
 | 
						return r, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *metadataStore) remove(id contextdir) error {
 | 
					func (s *metadataStore) remove(name string) error {
 | 
				
			||||||
	contextDir := s.contextDir(id)
 | 
						if err := os.RemoveAll(s.contextDir(contextdirOf(name))); err != nil {
 | 
				
			||||||
	return os.RemoveAll(contextDir)
 | 
							return errors.Wrapf(err, "failed to remove metadata")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *metadataStore) list() ([]Metadata, error) {
 | 
					func (s *metadataStore) list() ([]Metadata, error) {
 | 
				
			||||||
	ctxDirs, err := listRecursivelyMetadataDirs(s.root)
 | 
						ctxDirs, err := listRecursivelyMetadataDirs(s.root)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if os.IsNotExist(err) {
 | 
							if errors.Is(err, os.ErrNotExist) {
 | 
				
			||||||
			return nil, nil
 | 
								return nil, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var res []Metadata
 | 
						var res []Metadata
 | 
				
			||||||
	for _, dir := range ctxDirs {
 | 
						for _, dir := range ctxDirs {
 | 
				
			||||||
		c, err := s.get(contextdir(dir))
 | 
							c, err := s.getByID(contextdir(dir))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								if errors.Is(err, os.ErrNotExist) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil, errors.Wrap(err, "failed to read metadata")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		res = append(res, c)
 | 
							res = append(res, c)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -131,20 +147,13 @@ func listRecursivelyMetadataDirs(root string) ([]string, error) {
 | 
				
			||||||
				return nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			for _, s := range subs {
 | 
								for _, s := range subs {
 | 
				
			||||||
				result = append(result, fmt.Sprintf("%s/%s", fi.Name(), s))
 | 
									result = append(result, filepath.Join(fi.Name(), s))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return result, nil
 | 
						return result, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func convertContextDoesNotExist(err error) error {
 | 
					 | 
				
			||||||
	if os.IsNotExist(err) {
 | 
					 | 
				
			||||||
		return &contextDoesNotExistError{}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type untypedContextMetadata struct {
 | 
					type untypedContextMetadata struct {
 | 
				
			||||||
	Metadata  json.RawMessage            `json:"metadata,omitempty"`
 | 
						Metadata  json.RawMessage            `json:"metadata,omitempty"`
 | 
				
			||||||
	Endpoints map[string]json.RawMessage `json:"endpoints,omitempty"`
 | 
						Endpoints map[string]json.RawMessage `json:"endpoints,omitempty"`
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,6 @@ import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	_ "crypto/sha256" // ensure ids can be computed
 | 
						_ "crypto/sha256" // ensure ids can be computed
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
| 
						 | 
					@ -94,11 +93,11 @@ type ContextTLSData struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New creates a store from a given directory.
 | 
					// New creates a store from a given directory.
 | 
				
			||||||
// If the directory does not exist or is empty, initialize it
 | 
					// If the directory does not exist or is empty, initialize it
 | 
				
			||||||
func New(dir string, cfg Config) Store {
 | 
					func New(dir string, cfg Config) *ContextStore {
 | 
				
			||||||
	metaRoot := filepath.Join(dir, metadataDir)
 | 
						metaRoot := filepath.Join(dir, metadataDir)
 | 
				
			||||||
	tlsRoot := filepath.Join(dir, tlsDir)
 | 
						tlsRoot := filepath.Join(dir, tlsDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &store{
 | 
						return &ContextStore{
 | 
				
			||||||
		meta: &metadataStore{
 | 
							meta: &metadataStore{
 | 
				
			||||||
			root:   metaRoot,
 | 
								root:   metaRoot,
 | 
				
			||||||
			config: cfg,
 | 
								config: cfg,
 | 
				
			||||||
| 
						 | 
					@ -109,12 +108,14 @@ func New(dir string, cfg Config) Store {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type store struct {
 | 
					// ContextStore implements Store.
 | 
				
			||||||
 | 
					type ContextStore struct {
 | 
				
			||||||
	meta *metadataStore
 | 
						meta *metadataStore
 | 
				
			||||||
	tls  *tlsStore
 | 
						tls  *tlsStore
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) List() ([]Metadata, error) {
 | 
					// List return all contexts.
 | 
				
			||||||
 | 
					func (s *ContextStore) List() ([]Metadata, error) {
 | 
				
			||||||
	return s.meta.list()
 | 
						return s.meta.list()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,73 +132,82 @@ func Names(s Lister) ([]string, error) {
 | 
				
			||||||
	return names, nil
 | 
						return names, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) CreateOrUpdate(meta Metadata) error {
 | 
					// CreateOrUpdate creates or updates metadata for the context.
 | 
				
			||||||
 | 
					func (s *ContextStore) CreateOrUpdate(meta Metadata) error {
 | 
				
			||||||
	return s.meta.createOrUpdate(meta)
 | 
						return s.meta.createOrUpdate(meta)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) Remove(name string) error {
 | 
					// Remove deletes the context with the given name, if found.
 | 
				
			||||||
	id := contextdirOf(name)
 | 
					func (s *ContextStore) Remove(name string) error {
 | 
				
			||||||
	if err := s.meta.remove(id); err != nil {
 | 
						if err := s.meta.remove(name); err != nil {
 | 
				
			||||||
		return patchErrContextName(err, name)
 | 
							return errors.Wrapf(err, "failed to remove context %s", name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return patchErrContextName(s.tls.removeAllContextData(id), name)
 | 
						if err := s.tls.remove(name); err != nil {
 | 
				
			||||||
 | 
							return errors.Wrapf(err, "failed to remove context %s", name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) GetMetadata(name string) (Metadata, error) {
 | 
					// GetMetadata returns the metadata for the context with the given name.
 | 
				
			||||||
	res, err := s.meta.get(contextdirOf(name))
 | 
					// It returns an errdefs.ErrNotFound if the context was not found.
 | 
				
			||||||
	patchErrContextName(err, name)
 | 
					func (s *ContextStore) GetMetadata(name string) (Metadata, error) {
 | 
				
			||||||
	return res, err
 | 
						return s.meta.get(name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) ResetTLSMaterial(name string, data *ContextTLSData) error {
 | 
					// ResetTLSMaterial removes TLS data for all endpoints in the context and replaces
 | 
				
			||||||
	id := contextdirOf(name)
 | 
					// it with the new data.
 | 
				
			||||||
	if err := s.tls.removeAllContextData(id); err != nil {
 | 
					func (s *ContextStore) ResetTLSMaterial(name string, data *ContextTLSData) error {
 | 
				
			||||||
		return patchErrContextName(err, name)
 | 
						if err := s.tls.remove(name); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if data == nil {
 | 
						if data == nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for ep, files := range data.Endpoints {
 | 
						for ep, files := range data.Endpoints {
 | 
				
			||||||
		for fileName, data := range files.Files {
 | 
							for fileName, data := range files.Files {
 | 
				
			||||||
			if err := s.tls.createOrUpdate(id, ep, fileName, data); err != nil {
 | 
								if err := s.tls.createOrUpdate(name, ep, fileName, data); err != nil {
 | 
				
			||||||
				return patchErrContextName(err, name)
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) ResetEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error {
 | 
					// ResetEndpointTLSMaterial removes TLS data for the given context and endpoint,
 | 
				
			||||||
	id := contextdirOf(contextName)
 | 
					// and replaces it with the new data.
 | 
				
			||||||
	if err := s.tls.removeAllEndpointData(id, endpointName); err != nil {
 | 
					func (s *ContextStore) ResetEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error {
 | 
				
			||||||
		return patchErrContextName(err, contextName)
 | 
						if err := s.tls.removeEndpoint(contextName, endpointName); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if data == nil {
 | 
						if data == nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for fileName, data := range data.Files {
 | 
						for fileName, data := range data.Files {
 | 
				
			||||||
		if err := s.tls.createOrUpdate(id, endpointName, fileName, data); err != nil {
 | 
							if err := s.tls.createOrUpdate(contextName, endpointName, fileName, data); err != nil {
 | 
				
			||||||
			return patchErrContextName(err, contextName)
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) ListTLSFiles(name string) (map[string]EndpointFiles, error) {
 | 
					// ListTLSFiles returns the list of TLS files present for each endpoint in the
 | 
				
			||||||
	res, err := s.tls.listContextData(contextdirOf(name))
 | 
					// context.
 | 
				
			||||||
	return res, patchErrContextName(err, name)
 | 
					func (s *ContextStore) ListTLSFiles(name string) (map[string]EndpointFiles, error) {
 | 
				
			||||||
 | 
						return s.tls.listContextData(name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) GetTLSData(contextName, endpointName, fileName string) ([]byte, error) {
 | 
					// GetTLSData reads, and returns the content of the given fileName for an endpoint.
 | 
				
			||||||
	res, err := s.tls.getData(contextdirOf(contextName), endpointName, fileName)
 | 
					// It returns an errdefs.ErrNotFound if the file was not found.
 | 
				
			||||||
	return res, patchErrContextName(err, contextName)
 | 
					func (s *ContextStore) GetTLSData(contextName, endpointName, fileName string) ([]byte, error) {
 | 
				
			||||||
 | 
						return s.tls.getData(contextName, endpointName, fileName)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *store) GetStorageInfo(contextName string) StorageInfo {
 | 
					// GetStorageInfo returns the paths where the Metadata and TLS data are stored
 | 
				
			||||||
	dir := contextdirOf(contextName)
 | 
					// for the context.
 | 
				
			||||||
 | 
					func (s *ContextStore) GetStorageInfo(contextName string) StorageInfo {
 | 
				
			||||||
	return StorageInfo{
 | 
						return StorageInfo{
 | 
				
			||||||
		MetadataPath: s.meta.contextDir(dir),
 | 
							MetadataPath: s.meta.contextDir(contextdirOf(contextName)),
 | 
				
			||||||
		TLSPath:      s.tls.contextDir(dir),
 | 
							TLSPath:      s.tls.contextDir(contextName),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -210,7 +220,7 @@ func ValidateContextName(name string) error {
 | 
				
			||||||
		return errors.New(`"default" is a reserved context name`)
 | 
							return errors.New(`"default" is a reserved context name`)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !restrictedNameRegEx.MatchString(name) {
 | 
						if !restrictedNameRegEx.MatchString(name) {
 | 
				
			||||||
		return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
 | 
							return errors.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -236,7 +246,7 @@ func Export(name string, s Reader) io.ReadCloser {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err = tw.WriteHeader(&tar.Header{
 | 
							if err = tw.WriteHeader(&tar.Header{
 | 
				
			||||||
			Name: metaFile,
 | 
								Name: metaFile,
 | 
				
			||||||
			Mode: 0644,
 | 
								Mode: 0o644,
 | 
				
			||||||
			Size: int64(len(metaBytes)),
 | 
								Size: int64(len(metaBytes)),
 | 
				
			||||||
		}); err != nil {
 | 
							}); err != nil {
 | 
				
			||||||
			writer.CloseWithError(err)
 | 
								writer.CloseWithError(err)
 | 
				
			||||||
| 
						 | 
					@ -253,7 +263,7 @@ func Export(name string, s Reader) io.ReadCloser {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err = tw.WriteHeader(&tar.Header{
 | 
							if err = tw.WriteHeader(&tar.Header{
 | 
				
			||||||
			Name:     "tls",
 | 
								Name:     "tls",
 | 
				
			||||||
			Mode:     0700,
 | 
								Mode:     0o700,
 | 
				
			||||||
			Size:     0,
 | 
								Size:     0,
 | 
				
			||||||
			Typeflag: tar.TypeDir,
 | 
								Typeflag: tar.TypeDir,
 | 
				
			||||||
		}); err != nil {
 | 
							}); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -263,7 +273,7 @@ func Export(name string, s Reader) io.ReadCloser {
 | 
				
			||||||
		for endpointName, endpointFiles := range tlsFiles {
 | 
							for endpointName, endpointFiles := range tlsFiles {
 | 
				
			||||||
			if err = tw.WriteHeader(&tar.Header{
 | 
								if err = tw.WriteHeader(&tar.Header{
 | 
				
			||||||
				Name:     path.Join("tls", endpointName),
 | 
									Name:     path.Join("tls", endpointName),
 | 
				
			||||||
				Mode:     0700,
 | 
									Mode:     0o700,
 | 
				
			||||||
				Size:     0,
 | 
									Size:     0,
 | 
				
			||||||
				Typeflag: tar.TypeDir,
 | 
									Typeflag: tar.TypeDir,
 | 
				
			||||||
			}); err != nil {
 | 
								}); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -278,7 +288,7 @@ func Export(name string, s Reader) io.ReadCloser {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if err = tw.WriteHeader(&tar.Header{
 | 
									if err = tw.WriteHeader(&tar.Header{
 | 
				
			||||||
					Name: path.Join("tls", endpointName, fileName),
 | 
										Name: path.Join("tls", endpointName, fileName),
 | 
				
			||||||
					Mode: 0600,
 | 
										Mode: 0o600,
 | 
				
			||||||
					Size: int64(len(data)),
 | 
										Size: int64(len(data)),
 | 
				
			||||||
				}); err != nil {
 | 
									}); err != nil {
 | 
				
			||||||
					writer.CloseWithError(err)
 | 
										writer.CloseWithError(err)
 | 
				
			||||||
| 
						 | 
					@ -484,58 +494,18 @@ func importEndpointTLS(tlsData *ContextTLSData, path string, data []byte) error
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type setContextName interface {
 | 
					// IsErrContextDoesNotExist checks if the given error is a "context does not exist" condition.
 | 
				
			||||||
	setContext(name string)
 | 
					//
 | 
				
			||||||
}
 | 
					// Deprecated: use github.com/docker/docker/errdefs.IsNotFound()
 | 
				
			||||||
 | 
					 | 
				
			||||||
type contextDoesNotExistError struct {
 | 
					 | 
				
			||||||
	name string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (e *contextDoesNotExistError) Error() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("context %q does not exist", e.name)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (e *contextDoesNotExistError) setContext(name string) {
 | 
					 | 
				
			||||||
	e.name = name
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
 | 
					 | 
				
			||||||
func (e *contextDoesNotExistError) NotFound() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type tlsDataDoesNotExist interface {
 | 
					 | 
				
			||||||
	errdefs.ErrNotFound
 | 
					 | 
				
			||||||
	IsTLSDataDoesNotExist()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type tlsDataDoesNotExistError struct {
 | 
					 | 
				
			||||||
	context, endpoint, file string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (e *tlsDataDoesNotExistError) Error() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("tls data for %s/%s/%s does not exist", e.context, e.endpoint, e.file)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (e *tlsDataDoesNotExistError) setContext(name string) {
 | 
					 | 
				
			||||||
	e.context = name
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
 | 
					 | 
				
			||||||
func (e *tlsDataDoesNotExistError) NotFound() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsTLSDataDoesNotExist satisfies tlsDataDoesNotExist
 | 
					 | 
				
			||||||
func (e *tlsDataDoesNotExistError) IsTLSDataDoesNotExist() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsErrContextDoesNotExist checks if the given error is a "context does not exist" condition
 | 
					 | 
				
			||||||
func IsErrContextDoesNotExist(err error) bool {
 | 
					func IsErrContextDoesNotExist(err error) bool {
 | 
				
			||||||
	_, ok := err.(*contextDoesNotExistError)
 | 
						return errdefs.IsNotFound(err)
 | 
				
			||||||
	return ok
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsErrTLSDataDoesNotExist checks if the given error is a "context does not exist" condition
 | 
					// IsErrTLSDataDoesNotExist checks if the given error is a "context does not exist" condition
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: use github.com/docker/docker/errdefs.IsNotFound()
 | 
				
			||||||
func IsErrTLSDataDoesNotExist(err error) bool {
 | 
					func IsErrTLSDataDoesNotExist(err error) bool {
 | 
				
			||||||
	_, ok := err.(tlsDataDoesNotExist)
 | 
						return errdefs.IsNotFound(err)
 | 
				
			||||||
	return ok
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type contextdir string
 | 
					type contextdir string
 | 
				
			||||||
| 
						 | 
					@ -543,10 +513,3 @@ type contextdir string
 | 
				
			||||||
func contextdirOf(name string) contextdir {
 | 
					func contextdirOf(name string) contextdir {
 | 
				
			||||||
	return contextdir(digest.FromString(name).Encoded())
 | 
						return contextdir(digest.FromString(name).Encoded())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func patchErrContextName(err error, name string) error {
 | 
					 | 
				
			||||||
	if typed, ok := err.(setContextName); ok {
 | 
					 | 
				
			||||||
		typed.setContext(name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ func EndpointTypeGetter(name string, getter TypeGetter) NamedTypeGetter {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Config is used to configure the metadata marshaler of the context store
 | 
					// Config is used to configure the metadata marshaler of the context ContextStore
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	contextType   TypeGetter
 | 
						contextType   TypeGetter
 | 
				
			||||||
	endpointTypes map[string]TypeGetter
 | 
						endpointTypes map[string]TypeGetter
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,9 @@ package store
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/errdefs"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tlsDir = "tls"
 | 
					const tlsDir = "tls"
 | 
				
			||||||
| 
						 | 
					@ -11,69 +14,70 @@ type tlsStore struct {
 | 
				
			||||||
	root string
 | 
						root string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *tlsStore) contextDir(id contextdir) string {
 | 
					func (s *tlsStore) contextDir(name string) string {
 | 
				
			||||||
	return filepath.Join(s.root, string(id))
 | 
						return filepath.Join(s.root, string(contextdirOf(name)))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *tlsStore) endpointDir(contextID contextdir, name string) string {
 | 
					func (s *tlsStore) endpointDir(name, endpointName string) string {
 | 
				
			||||||
	return filepath.Join(s.root, string(contextID), name)
 | 
						return filepath.Join(s.contextDir(name), endpointName)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *tlsStore) filePath(contextID contextdir, endpointName, filename string) string {
 | 
					func (s *tlsStore) createOrUpdate(name, endpointName, filename string, data []byte) error {
 | 
				
			||||||
	return filepath.Join(s.root, string(contextID), endpointName, filename)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *tlsStore) createOrUpdate(contextID contextdir, endpointName, filename string, data []byte) error {
 | 
					 | 
				
			||||||
	epdir := s.endpointDir(contextID, endpointName)
 | 
					 | 
				
			||||||
	parentOfRoot := filepath.Dir(s.root)
 | 
						parentOfRoot := filepath.Dir(s.root)
 | 
				
			||||||
	if err := os.MkdirAll(parentOfRoot, 0755); err != nil {
 | 
						if err := os.MkdirAll(parentOfRoot, 0o755); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := os.MkdirAll(epdir, 0700); err != nil {
 | 
						endpointDir := s.endpointDir(name, endpointName)
 | 
				
			||||||
 | 
						if err := os.MkdirAll(endpointDir, 0o700); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return os.WriteFile(s.filePath(contextID, endpointName, filename), data, 0600)
 | 
						return os.WriteFile(filepath.Join(endpointDir, filename), data, 0o600)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *tlsStore) getData(contextID contextdir, endpointName, filename string) ([]byte, error) {
 | 
					func (s *tlsStore) getData(name, endpointName, filename string) ([]byte, error) {
 | 
				
			||||||
	data, err := os.ReadFile(s.filePath(contextID, endpointName, filename))
 | 
						data, err := os.ReadFile(filepath.Join(s.endpointDir(name, endpointName), filename))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, convertTLSDataDoesNotExist(endpointName, filename, err)
 | 
							if os.IsNotExist(err) {
 | 
				
			||||||
 | 
								return nil, errdefs.NotFound(errors.Errorf("TLS data for %s/%s/%s does not exist", name, endpointName, filename))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, errors.Wrapf(err, "failed to read TLS data for endpoint %s", endpointName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return data, nil
 | 
						return data, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error { //nolint:unused
 | 
					// remove deletes all TLS data for the given context.
 | 
				
			||||||
	err := os.Remove(s.filePath(contextID, endpointName, filename))
 | 
					func (s *tlsStore) remove(name string) error {
 | 
				
			||||||
	if os.IsNotExist(err) {
 | 
						if err := os.RemoveAll(s.contextDir(name)); err != nil {
 | 
				
			||||||
		return nil
 | 
							return errors.Wrapf(err, "failed to remove TLS data")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return err
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *tlsStore) removeAllEndpointData(contextID contextdir, endpointName string) error {
 | 
					func (s *tlsStore) removeEndpoint(name, endpointName string) error {
 | 
				
			||||||
	return os.RemoveAll(s.endpointDir(contextID, endpointName))
 | 
						if err := os.RemoveAll(s.endpointDir(name, endpointName)); err != nil {
 | 
				
			||||||
 | 
							return errors.Wrapf(err, "failed to remove TLS data for endpoint %s", endpointName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *tlsStore) removeAllContextData(contextID contextdir) error {
 | 
					func (s *tlsStore) listContextData(name string) (map[string]EndpointFiles, error) {
 | 
				
			||||||
	return os.RemoveAll(s.contextDir(contextID))
 | 
						contextDir := s.contextDir(name)
 | 
				
			||||||
}
 | 
						epFSs, err := os.ReadDir(contextDir)
 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *tlsStore) listContextData(contextID contextdir) (map[string]EndpointFiles, error) {
 | 
					 | 
				
			||||||
	epFSs, err := os.ReadDir(s.contextDir(contextID))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if os.IsNotExist(err) {
 | 
							if os.IsNotExist(err) {
 | 
				
			||||||
			return map[string]EndpointFiles{}, nil
 | 
								return map[string]EndpointFiles{}, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil, err
 | 
							return nil, errors.Wrapf(err, "failed to list TLS files for context %s", name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	r := make(map[string]EndpointFiles)
 | 
						r := make(map[string]EndpointFiles)
 | 
				
			||||||
	for _, epFS := range epFSs {
 | 
						for _, epFS := range epFSs {
 | 
				
			||||||
		if epFS.IsDir() {
 | 
							if epFS.IsDir() {
 | 
				
			||||||
			epDir := s.endpointDir(contextID, epFS.Name())
 | 
								fss, err := os.ReadDir(filepath.Join(contextDir, epFS.Name()))
 | 
				
			||||||
			fss, err := os.ReadDir(epDir)
 | 
								if os.IsNotExist(err) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									return nil, errors.Wrapf(err, "failed to list TLS files for endpoint %s", epFS.Name())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			var files EndpointFiles
 | 
								var files EndpointFiles
 | 
				
			||||||
			for _, fs := range fss {
 | 
								for _, fs := range fss {
 | 
				
			||||||
| 
						 | 
					@ -89,10 +93,3 @@ func (s *tlsStore) listContextData(contextID contextdir) (map[string]EndpointFil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EndpointFiles is a slice of strings representing file names
 | 
					// EndpointFiles is a slice of strings representing file names
 | 
				
			||||||
type EndpointFiles []string
 | 
					type EndpointFiles []string
 | 
				
			||||||
 | 
					 | 
				
			||||||
func convertTLSDataDoesNotExist(endpoint, file string, err error) error {
 | 
					 | 
				
			||||||
	if os.IsNotExist(err) {
 | 
					 | 
				
			||||||
		return &tlsDataDoesNotExistError{endpoint: endpoint, file: file}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,14 +45,14 @@ func (data *TLSData) ToStoreTLSData() *store.EndpointTLSData {
 | 
				
			||||||
func LoadTLSData(s store.Reader, contextName, endpointName string) (*TLSData, error) {
 | 
					func LoadTLSData(s store.Reader, contextName, endpointName string) (*TLSData, error) {
 | 
				
			||||||
	tlsFiles, err := s.ListTLSFiles(contextName)
 | 
						tlsFiles, err := s.ListTLSFiles(contextName)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrapf(err, "failed to retrieve context tls files for context %q", contextName)
 | 
							return nil, errors.Wrapf(err, "failed to retrieve TLS files for context %q", contextName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if epTLSFiles, ok := tlsFiles[endpointName]; ok {
 | 
						if epTLSFiles, ok := tlsFiles[endpointName]; ok {
 | 
				
			||||||
		var tlsData TLSData
 | 
							var tlsData TLSData
 | 
				
			||||||
		for _, f := range epTLSFiles {
 | 
							for _, f := range epTLSFiles {
 | 
				
			||||||
			data, err := s.GetTLSData(contextName, endpointName, f)
 | 
								data, err := s.GetTLSData(contextName, endpointName, f)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, errors.Wrapf(err, "failed to retrieve context tls data for file %q of context %q", f, contextName)
 | 
									return nil, errors.Wrapf(err, "failed to retrieve TLS data (%s) for context %q", f, contextName)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			switch f {
 | 
								switch f {
 | 
				
			||||||
			case caKey:
 | 
								case caKey:
 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,7 @@ func LoadTLSData(s store.Reader, contextName, endpointName string) (*TLSData, er
 | 
				
			||||||
			case keyKey:
 | 
								case keyKey:
 | 
				
			||||||
				tlsData.Key = data
 | 
									tlsData.Key = data
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				logrus.Warnf("unknown file %s in context %s tls bundle", f, contextName)
 | 
									logrus.Warnf("unknown file in context %s TLS bundle: %s", contextName, f)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return &tlsData, nil
 | 
							return &tlsData, nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +0,0 @@
 | 
				
			||||||
package flags
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ClientOptions are the options used to configure the client cli
 | 
					 | 
				
			||||||
type ClientOptions struct {
 | 
					 | 
				
			||||||
	Common    *CommonOptions
 | 
					 | 
				
			||||||
	ConfigDir string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewClientOptions returns a new ClientOptions
 | 
					 | 
				
			||||||
func NewClientOptions() *ClientOptions {
 | 
					 | 
				
			||||||
	return &ClientOptions{Common: NewCommonOptions()}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -43,8 +43,8 @@ var (
 | 
				
			||||||
	dockerTLS = os.Getenv("DOCKER_TLS") != ""
 | 
						dockerTLS = os.Getenv("DOCKER_TLS") != ""
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommonOptions are options common to both the client and the daemon.
 | 
					// ClientOptions are the options used to configure the client cli.
 | 
				
			||||||
type CommonOptions struct {
 | 
					type ClientOptions struct {
 | 
				
			||||||
	Debug      bool
 | 
						Debug      bool
 | 
				
			||||||
	Hosts      []string
 | 
						Hosts      []string
 | 
				
			||||||
	LogLevel   string
 | 
						LogLevel   string
 | 
				
			||||||
| 
						 | 
					@ -52,59 +52,60 @@ type CommonOptions struct {
 | 
				
			||||||
	TLSVerify  bool
 | 
						TLSVerify  bool
 | 
				
			||||||
	TLSOptions *tlsconfig.Options
 | 
						TLSOptions *tlsconfig.Options
 | 
				
			||||||
	Context    string
 | 
						Context    string
 | 
				
			||||||
 | 
						ConfigDir  string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewCommonOptions returns a new CommonOptions
 | 
					// NewClientOptions returns a new ClientOptions.
 | 
				
			||||||
func NewCommonOptions() *CommonOptions {
 | 
					func NewClientOptions() *ClientOptions {
 | 
				
			||||||
	return &CommonOptions{}
 | 
						return &ClientOptions{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InstallFlags adds flags for the common options on the FlagSet
 | 
					// InstallFlags adds flags for the common options on the FlagSet
 | 
				
			||||||
func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
 | 
					func (o *ClientOptions) InstallFlags(flags *pflag.FlagSet) {
 | 
				
			||||||
	if dockerCertPath == "" {
 | 
						if dockerCertPath == "" {
 | 
				
			||||||
		dockerCertPath = config.Dir()
 | 
							dockerCertPath = config.Dir()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode")
 | 
						flags.BoolVarP(&o.Debug, "debug", "D", false, "Enable debug mode")
 | 
				
			||||||
	flags.StringVarP(&commonOpts.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`)
 | 
						flags.StringVarP(&o.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`)
 | 
				
			||||||
	flags.BoolVar(&commonOpts.TLS, "tls", dockerTLS, "Use TLS; implied by --tlsverify")
 | 
						flags.BoolVar(&o.TLS, "tls", dockerTLS, "Use TLS; implied by --tlsverify")
 | 
				
			||||||
	flags.BoolVar(&commonOpts.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote")
 | 
						flags.BoolVar(&o.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")
 | 
						// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	commonOpts.TLSOptions = &tlsconfig.Options{
 | 
						o.TLSOptions = &tlsconfig.Options{
 | 
				
			||||||
		CAFile:   filepath.Join(dockerCertPath, DefaultCaFile),
 | 
							CAFile:   filepath.Join(dockerCertPath, DefaultCaFile),
 | 
				
			||||||
		CertFile: filepath.Join(dockerCertPath, DefaultCertFile),
 | 
							CertFile: filepath.Join(dockerCertPath, DefaultCertFile),
 | 
				
			||||||
		KeyFile:  filepath.Join(dockerCertPath, DefaultKeyFile),
 | 
							KeyFile:  filepath.Join(dockerCertPath, DefaultKeyFile),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tlsOptions := commonOpts.TLSOptions
 | 
						tlsOptions := o.TLSOptions
 | 
				
			||||||
	flags.Var(opts.NewQuotedString(&tlsOptions.CAFile), "tlscacert", "Trust certs signed only by this CA")
 | 
						flags.Var(opts.NewQuotedString(&tlsOptions.CAFile), "tlscacert", "Trust certs signed only by this CA")
 | 
				
			||||||
	flags.Var(opts.NewQuotedString(&tlsOptions.CertFile), "tlscert", "Path to TLS certificate file")
 | 
						flags.Var(opts.NewQuotedString(&tlsOptions.CertFile), "tlscert", "Path to TLS certificate file")
 | 
				
			||||||
	flags.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "tlskey", "Path to TLS key file")
 | 
						flags.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "tlskey", "Path to TLS key file")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// opts.ValidateHost is not used here, so as to allow connection helpers
 | 
						// opts.ValidateHost is not used here, so as to allow connection helpers
 | 
				
			||||||
	hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, nil)
 | 
						hostOpt := opts.NewNamedListOptsRef("hosts", &o.Hosts, nil)
 | 
				
			||||||
	flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
 | 
						flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
 | 
				
			||||||
	flags.StringVarP(&commonOpts.Context, "context", "c", "",
 | 
						flags.StringVarP(&o.Context, "context", "c", "",
 | 
				
			||||||
		`Name of the context to use to connect to the daemon (overrides `+client.EnvOverrideHost+` env var and default context set with "docker context use")`)
 | 
							`Name of the context to use to connect to the daemon (overrides `+client.EnvOverrideHost+` env var and default context set with "docker context use")`)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetDefaultOptions sets default values for options after flag parsing is
 | 
					// SetDefaultOptions sets default values for options after flag parsing is
 | 
				
			||||||
// complete
 | 
					// complete
 | 
				
			||||||
func (commonOpts *CommonOptions) SetDefaultOptions(flags *pflag.FlagSet) {
 | 
					func (o *ClientOptions) SetDefaultOptions(flags *pflag.FlagSet) {
 | 
				
			||||||
	// Regardless of whether the user sets it to true or false, if they
 | 
						// Regardless of whether the user sets it to true or false, if they
 | 
				
			||||||
	// specify --tlsverify at all then we need to turn on TLS
 | 
						// specify --tlsverify at all then we need to turn on TLS
 | 
				
			||||||
	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need
 | 
						// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need
 | 
				
			||||||
	// to check that here as well
 | 
						// to check that here as well
 | 
				
			||||||
	if flags.Changed(FlagTLSVerify) || commonOpts.TLSVerify {
 | 
						if flags.Changed(FlagTLSVerify) || o.TLSVerify {
 | 
				
			||||||
		commonOpts.TLS = true
 | 
							o.TLS = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !commonOpts.TLS {
 | 
						if !o.TLS {
 | 
				
			||||||
		commonOpts.TLSOptions = nil
 | 
							o.TLSOptions = nil
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		tlsOptions := commonOpts.TLSOptions
 | 
							tlsOptions := o.TLSOptions
 | 
				
			||||||
		tlsOptions.InsecureSkipVerify = !commonOpts.TLSVerify
 | 
							tlsOptions.InsecureSkipVerify = !o.TLSVerify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Reset CertFile and KeyFile to empty string if the user did not specify
 | 
							// Reset CertFile and KeyFile to empty string if the user did not specify
 | 
				
			||||||
		// the respective flags and the respective default files were not found.
 | 
							// the respective flags and the respective default files were not found.
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					package flags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CommonOptions are options common to both the client and the daemon.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: use [ClientOptions].
 | 
				
			||||||
 | 
					type CommonOptions = ClientOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewCommonOptions returns a new CommonOptions
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: use [NewClientOptions].
 | 
				
			||||||
 | 
					var NewCommonOptions = NewClientOptions
 | 
				
			||||||
| 
						 | 
					@ -136,12 +136,12 @@ func (s *fsStore) Save(listRef reference.Reference, manifest reference.Reference
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return os.WriteFile(filename, bytes, 0644)
 | 
						return os.WriteFile(filename, bytes, 0o644)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *fsStore) createManifestListDirectory(transaction string) error {
 | 
					func (s *fsStore) createManifestListDirectory(transaction string) error {
 | 
				
			||||||
	path := filepath.Join(s.root, makeFilesafeName(transaction))
 | 
						path := filepath.Join(s.root, makeFilesafeName(transaction))
 | 
				
			||||||
	return os.MkdirAll(path, 0755)
 | 
						return os.MkdirAll(path, 0o755)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func manifestToFilename(root, manifestList, manifest string) string {
 | 
					func manifestToFilename(root, manifestList, manifest string) string {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,7 +102,7 @@ func GetNotaryRepository(in io.Reader, out io.Writer, userAgent string, repoInfo
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var cfg = tlsconfig.ClientDefault()
 | 
						cfg := tlsconfig.ClientDefault()
 | 
				
			||||||
	cfg.InsecureSkipVerify = !repoInfo.Index.Secure
 | 
						cfg.InsecureSkipVerify = !repoInfo.Index.Secure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get certificate base directory
 | 
						// Get certificate base directory
 | 
				
			||||||
| 
						 | 
					@ -136,7 +136,7 @@ func GetNotaryRepository(in io.Reader, out io.Writer, userAgent string, repoInfo
 | 
				
			||||||
		Timeout:   5 * time.Second,
 | 
							Timeout:   5 * time.Second,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	endpointStr := server + "/v2/"
 | 
						endpointStr := server + "/v2/"
 | 
				
			||||||
	req, err := http.NewRequest("GET", endpointStr, nil)
 | 
						req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -287,7 +287,6 @@ func GetSignableRoles(repo client.Repository, target *client.Target) ([]data.Rol
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return signableRoles, nil
 | 
						return signableRoles, nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ImageRefAndAuth contains all reference information and the auth config for an image request
 | 
					// ImageRefAndAuth contains all reference information and the auth config for an image request
 | 
				
			||||||
| 
						 | 
					@ -384,5 +383,4 @@ func (imgRefAuth *ImageRefAndAuth) Digest() digest.Digest {
 | 
				
			||||||
// Name returns the image name used to initialize the ImageRefAndAuth
 | 
					// Name returns the image name used to initialize the ImageRefAndAuth
 | 
				
			||||||
func (imgRefAuth *ImageRefAndAuth) Name() string {
 | 
					func (imgRefAuth *ImageRefAndAuth) Name() string {
 | 
				
			||||||
	return imgRefAuth.original
 | 
						return imgRefAuth.original
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ func (o *ConfigOpt) Set(value string) error {
 | 
				
			||||||
		File: &swarmtypes.ConfigReferenceFileTarget{
 | 
							File: &swarmtypes.ConfigReferenceFileTarget{
 | 
				
			||||||
			UID:  "0",
 | 
								UID:  "0",
 | 
				
			||||||
			GID:  "0",
 | 
								GID:  "0",
 | 
				
			||||||
			Mode: 0444,
 | 
								Mode: 0o444,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,7 +86,7 @@ func parseDockerDaemonHost(addr string) (string, error) {
 | 
				
			||||||
	case "ssh":
 | 
						case "ssh":
 | 
				
			||||||
		return addr, nil
 | 
							return addr, nil
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return "", fmt.Errorf("Invalid bind address format: %s", addr)
 | 
							return "", fmt.Errorf("invalid bind address format: %s", addr)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,7 +97,7 @@ func parseDockerDaemonHost(addr string) (string, error) {
 | 
				
			||||||
func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) {
 | 
					func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) {
 | 
				
			||||||
	addr = strings.TrimPrefix(addr, proto+"://")
 | 
						addr = strings.TrimPrefix(addr, proto+"://")
 | 
				
			||||||
	if strings.Contains(addr, "://") {
 | 
						if strings.Contains(addr, "://") {
 | 
				
			||||||
		return "", fmt.Errorf("Invalid proto, expected %s: %s", proto, addr)
 | 
							return "", fmt.Errorf("invalid proto, expected %s: %s", proto, addr)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if addr == "" {
 | 
						if addr == "" {
 | 
				
			||||||
		addr = defaultAddr
 | 
							addr = defaultAddr
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,7 @@ func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	addr := strings.TrimPrefix(tryAddr, "tcp://")
 | 
						addr := strings.TrimPrefix(tryAddr, "tcp://")
 | 
				
			||||||
	if strings.Contains(addr, "://") || addr == "" {
 | 
						if strings.Contains(addr, "://") || addr == "" {
 | 
				
			||||||
		return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr)
 | 
							return "", fmt.Errorf("invalid proto, expected tcp: %s", tryAddr)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
 | 
						defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
 | 
				
			||||||
| 
						 | 
					@ -141,7 +141,7 @@ func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
 | 
				
			||||||
		host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort))
 | 
							host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
 | 
							return "", fmt.Errorf("invalid bind address format: %s", tryAddr)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if host == "" {
 | 
						if host == "" {
 | 
				
			||||||
| 
						 | 
					@ -152,7 +152,7 @@ func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p, err := strconv.Atoi(port)
 | 
						p, err := strconv.Atoi(port)
 | 
				
			||||||
	if err != nil && p == 0 {
 | 
						if err != nil && p == 0 {
 | 
				
			||||||
		return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
 | 
							return "", fmt.Errorf("invalid bind address format: %s", tryAddr)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil
 | 
						return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -230,7 +230,7 @@ type ValidatorFctListType func(val string) ([]string, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateIPAddress validates an Ip address.
 | 
					// ValidateIPAddress validates an Ip address.
 | 
				
			||||||
func ValidateIPAddress(val string) (string, error) {
 | 
					func ValidateIPAddress(val string) (string, error) {
 | 
				
			||||||
	var ip = net.ParseIP(strings.TrimSpace(val))
 | 
						ip := net.ParseIP(strings.TrimSpace(val))
 | 
				
			||||||
	if ip != nil {
 | 
						if ip != nil {
 | 
				
			||||||
		return ip.String(), nil
 | 
							return ip.String(), nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,79 +0,0 @@
 | 
				
			||||||
package opts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/docker/docker/api/types"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RuntimeOpt defines a map of Runtimes
 | 
					 | 
				
			||||||
type RuntimeOpt struct {
 | 
					 | 
				
			||||||
	name             string
 | 
					 | 
				
			||||||
	stockRuntimeName string
 | 
					 | 
				
			||||||
	values           *map[string]types.Runtime
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewNamedRuntimeOpt creates a new RuntimeOpt
 | 
					 | 
				
			||||||
func NewNamedRuntimeOpt(name string, ref *map[string]types.Runtime, stockRuntime string) *RuntimeOpt {
 | 
					 | 
				
			||||||
	if ref == nil {
 | 
					 | 
				
			||||||
		ref = &map[string]types.Runtime{}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &RuntimeOpt{name: name, values: ref, stockRuntimeName: stockRuntime}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Name returns the name of the NamedListOpts in the configuration.
 | 
					 | 
				
			||||||
func (o *RuntimeOpt) Name() string {
 | 
					 | 
				
			||||||
	return o.name
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Set validates and updates the list of Runtimes
 | 
					 | 
				
			||||||
func (o *RuntimeOpt) Set(val string) error {
 | 
					 | 
				
			||||||
	parts := strings.SplitN(val, "=", 2)
 | 
					 | 
				
			||||||
	if len(parts) != 2 {
 | 
					 | 
				
			||||||
		return fmt.Errorf("invalid runtime argument: %s", val)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	parts[0] = strings.TrimSpace(parts[0])
 | 
					 | 
				
			||||||
	parts[1] = strings.TrimSpace(parts[1])
 | 
					 | 
				
			||||||
	if parts[0] == "" || parts[1] == "" {
 | 
					 | 
				
			||||||
		return fmt.Errorf("invalid runtime argument: %s", val)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	parts[0] = strings.ToLower(parts[0])
 | 
					 | 
				
			||||||
	if parts[0] == o.stockRuntimeName {
 | 
					 | 
				
			||||||
		return fmt.Errorf("runtime name '%s' is reserved", o.stockRuntimeName)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if _, ok := (*o.values)[parts[0]]; ok {
 | 
					 | 
				
			||||||
		return fmt.Errorf("runtime '%s' was already defined", parts[0])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	(*o.values)[parts[0]] = types.Runtime{Path: parts[1]}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// String returns Runtime values as a string.
 | 
					 | 
				
			||||||
func (o *RuntimeOpt) String() string {
 | 
					 | 
				
			||||||
	var out []string
 | 
					 | 
				
			||||||
	for k := range *o.values {
 | 
					 | 
				
			||||||
		out = append(out, k)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return fmt.Sprintf("%v", out)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetMap returns a map of Runtimes (name: path)
 | 
					 | 
				
			||||||
func (o *RuntimeOpt) GetMap() map[string]types.Runtime {
 | 
					 | 
				
			||||||
	if o.values != nil {
 | 
					 | 
				
			||||||
		return *o.values
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return map[string]types.Runtime{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Type returns the type of the option
 | 
					 | 
				
			||||||
func (o *RuntimeOpt) Type() string {
 | 
					 | 
				
			||||||
	return "runtime"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ func (o *SecretOpt) Set(value string) error {
 | 
				
			||||||
		File: &swarmtypes.SecretReferenceFileTarget{
 | 
							File: &swarmtypes.SecretReferenceFileTarget{
 | 
				
			||||||
			UID:  "0",
 | 
								UID:  "0",
 | 
				
			||||||
			GID:  "0",
 | 
								GID:  "0",
 | 
				
			||||||
			Mode: 0444,
 | 
								Mode: 0o444,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,8 +169,8 @@ func Erase(helper Helper, reader io.Reader) error {
 | 
				
			||||||
	return helper.Delete(serverURL)
 | 
						return helper.Delete(serverURL)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//List returns all the serverURLs of keys in
 | 
					// List returns all the serverURLs of keys in
 | 
				
			||||||
//the OS store as a list of strings
 | 
					// the OS store as a list of strings
 | 
				
			||||||
func List(helper Helper, writer io.Writer) error {
 | 
					func List(helper Helper, writer io.Writer) error {
 | 
				
			||||||
	accts, err := helper.List()
 | 
						accts, err := helper.List()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -179,8 +179,8 @@ func List(helper Helper, writer io.Writer) error {
 | 
				
			||||||
	return json.NewEncoder(writer).Encode(accts)
 | 
						return json.NewEncoder(writer).Encode(accts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//PrintVersion outputs the current version.
 | 
					// PrintVersion outputs the current version.
 | 
				
			||||||
func PrintVersion(writer io.Writer) error {
 | 
					func PrintVersion(writer io.Writer) error {
 | 
				
			||||||
	fmt.Fprintln(writer, Version)
 | 
						fmt.Fprintf(writer, "%s (%s) %s\n", Name, Package, Version)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,16 @@
 | 
				
			||||||
package credentials
 | 
					package credentials
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Version holds a string describing the current version
 | 
					var (
 | 
				
			||||||
const Version = "0.6.4"
 | 
						// Name is filled at linking time
 | 
				
			||||||
 | 
						Name = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Package is filled at linking time
 | 
				
			||||||
 | 
						Package = "github.com/docker/docker-credential-helpers"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Version holds the complete version number. Filled in at linking time.
 | 
				
			||||||
 | 
						Version = "v0.0.0+unknown"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Revision is filled with the VCS (e.g. git) revision being used to build
 | 
				
			||||||
 | 
						// the program at linking time.
 | 
				
			||||||
 | 
						Revision = ""
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -229,13 +229,8 @@ func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decomp
 | 
				
			||||||
	dest = filepath.Clean(dest)
 | 
						dest = filepath.Clean(dest)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We need to be able to set any perms
 | 
						// We need to be able to set any perms
 | 
				
			||||||
	if runtime.GOOS != "windows" {
 | 
						restore := overrideUmask(0)
 | 
				
			||||||
		oldmask, err := system.Umask(0)
 | 
						defer restore()
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return 0, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		defer system.Umask(oldmask)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if decompress {
 | 
						if decompress {
 | 
				
			||||||
		decompLayer, err := DecompressStream(layer)
 | 
							decompLayer, err := DecompressStream(layer)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package archive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "golang.org/x/sys/unix"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// overrideUmask sets current process's file mode creation mask to newmask
 | 
				
			||||||
 | 
					// and returns a function to restore it.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// WARNING for readers stumbling upon this code. Changing umask in a multi-
 | 
				
			||||||
 | 
					// threaded environment isn't safe. Don't use this without understanding the
 | 
				
			||||||
 | 
					// risks, and don't export this function for others to use (we shouldn't even
 | 
				
			||||||
 | 
					// be using this ourself).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// FIXME(thaJeztah): we should get rid of these hacks if possible.
 | 
				
			||||||
 | 
					func overrideUmask(newMask int) func() {
 | 
				
			||||||
 | 
						oldMask := unix.Umask(newMask)
 | 
				
			||||||
 | 
						return func() {
 | 
				
			||||||
 | 
							unix.Umask(oldMask)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					package archive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// overrideUmask is a no-op on windows.
 | 
				
			||||||
 | 
					func overrideUmask(newmask int) func() {
 | 
				
			||||||
 | 
						return func() {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
| 
						 | 
					@ -199,7 +200,7 @@ func callGetent(database, key string) (io.Reader, error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	out, err := execCmd(getentCmd, database, key)
 | 
						out, err := execCmd(getentCmd, database, key)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		exitCode, errC := system.GetExitCode(err)
 | 
							exitCode, errC := getExitCode(err)
 | 
				
			||||||
		if errC != nil {
 | 
							if errC != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -217,6 +218,18 @@ func callGetent(database, key string) (io.Reader, error) {
 | 
				
			||||||
	return bytes.NewReader(out), nil
 | 
						return bytes.NewReader(out), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getExitCode returns the ExitStatus of the specified error if its type is
 | 
				
			||||||
 | 
					// exec.ExitError, returns 0 and an error otherwise.
 | 
				
			||||||
 | 
					func getExitCode(err error) (int, error) {
 | 
				
			||||||
 | 
						exitCode := 0
 | 
				
			||||||
 | 
						if exiterr, ok := err.(*exec.ExitError); ok {
 | 
				
			||||||
 | 
							if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
 | 
				
			||||||
 | 
								return procExit.ExitStatus(), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return exitCode, fmt.Errorf("failed to get exit code")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested
 | 
					// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested
 | 
				
			||||||
// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the
 | 
					// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the
 | 
				
			||||||
// dir is on an NFS share, so don't call chown unless we absolutely must.
 | 
					// dir is on an NFS share, so don't call chown unless we absolutely must.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +0,0 @@
 | 
				
			||||||
package system // import "github.com/docker/docker/pkg/system"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os/exec"
 | 
					 | 
				
			||||||
	"syscall"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetExitCode returns the ExitStatus of the specified error if its type is
 | 
					 | 
				
			||||||
// exec.ExitError, returns 0 and an error otherwise.
 | 
					 | 
				
			||||||
func GetExitCode(err error) (int, error) {
 | 
					 | 
				
			||||||
	exitCode := 0
 | 
					 | 
				
			||||||
	if exiterr, ok := err.(*exec.ExitError); ok {
 | 
					 | 
				
			||||||
		if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
 | 
					 | 
				
			||||||
			return procExit.ExitStatus(), nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return exitCode, fmt.Errorf("failed to get exit code")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -20,12 +20,12 @@ func (s StatT) Size() int64 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Mode returns file's permission mode.
 | 
					// Mode returns file's permission mode.
 | 
				
			||||||
func (s StatT) Mode() os.FileMode {
 | 
					func (s StatT) Mode() os.FileMode {
 | 
				
			||||||
	return os.FileMode(s.mode)
 | 
						return s.mode
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Mtim returns file's last modification time.
 | 
					// Mtim returns file's last modification time.
 | 
				
			||||||
func (s StatT) Mtim() time.Time {
 | 
					func (s StatT) Mtim() time.Time {
 | 
				
			||||||
	return time.Time(s.mtim)
 | 
						return s.mtim
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Stat takes a path to a file and returns
 | 
					// Stat takes a path to a file and returns
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +0,0 @@
 | 
				
			||||||
//go:build !windows
 | 
					 | 
				
			||||||
// +build !windows
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package system // import "github.com/docker/docker/pkg/system"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"golang.org/x/sys/unix"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Umask sets current process's file mode creation mask to newmask
 | 
					 | 
				
			||||||
// and returns oldmask.
 | 
					 | 
				
			||||||
func Umask(newmask int) (oldmask int, err error) {
 | 
					 | 
				
			||||||
	return unix.Umask(newmask), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +0,0 @@
 | 
				
			||||||
package system // import "github.com/docker/docker/pkg/system"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Umask is not supported on the windows platform.
 | 
					 | 
				
			||||||
func Umask(newmask int) (oldmask int, err error) {
 | 
					 | 
				
			||||||
	// should not be called on cli code path
 | 
					 | 
				
			||||||
	return 0, ErrNotSupportedPlatform
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,8 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *defaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
 | 
					func (s *defaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
 | 
				
			||||||
 | 
						ana := s.config.allowNondistributableArtifacts(hostname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if hostname == DefaultNamespace || hostname == IndexHostname {
 | 
						if hostname == DefaultNamespace || hostname == IndexHostname {
 | 
				
			||||||
		for _, mirror := range s.config.Mirrors {
 | 
							for _, mirror := range s.config.Mirrors {
 | 
				
			||||||
			if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") {
 | 
								if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") {
 | 
				
			||||||
| 
						 | 
					@ -35,6 +37,8 @@ func (s *defaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
 | 
				
			||||||
			Official:     true,
 | 
								Official:     true,
 | 
				
			||||||
			TrimHostname: true,
 | 
								TrimHostname: true,
 | 
				
			||||||
			TLSConfig:    tlsconfig.ServerDefault(),
 | 
								TLSConfig:    tlsconfig.ServerDefault(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								AllowNondistributableArtifacts: ana,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return endpoints, nil
 | 
							return endpoints, nil
 | 
				
			||||||
| 
						 | 
					@ -45,7 +49,6 @@ func (s *defaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ana := s.config.allowNondistributableArtifacts(hostname)
 | 
					 | 
				
			||||||
	endpoints = []APIEndpoint{
 | 
						endpoints = []APIEndpoint{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			URL: &url.URL{
 | 
								URL: &url.URL{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
language: go
 | 
					language: go
 | 
				
			||||||
go:
 | 
					go:
 | 
				
			||||||
  - 1.10.x
 | 
					  - 1.14.x
 | 
				
			||||||
  - 1.11.x
 | 
					  - 1.15.x
 | 
				
			||||||
script: go test -v -check.vv -race ./...
 | 
					script: go test -v -check.vv -race ./...
 | 
				
			||||||
sudo: false
 | 
					sudo: false
 | 
				
			||||||
notifications:
 | 
					notifications:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ clone_folder: 'c:\gopath\src\github.com\gofrs\flock'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  GOPATH: 'c:\gopath'
 | 
					  GOPATH: 'c:\gopath'
 | 
				
			||||||
  GOVERSION: '1.11'
 | 
					  GOVERSION: '1.15'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
init:
 | 
					init:
 | 
				
			||||||
  - git config --global core.autocrlf input
 | 
					  - git config --global core.autocrlf input
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@ package flock
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -116,7 +117,15 @@ func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Durati
 | 
				
			||||||
func (f *Flock) setFh() error {
 | 
					func (f *Flock) setFh() error {
 | 
				
			||||||
	// open a new os.File instance
 | 
						// open a new os.File instance
 | 
				
			||||||
	// create it if it doesn't exist, and open the file read-only.
 | 
						// create it if it doesn't exist, and open the file read-only.
 | 
				
			||||||
	fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDONLY, os.FileMode(0600))
 | 
						flags := os.O_CREATE
 | 
				
			||||||
 | 
						if runtime.GOOS == "aix" {
 | 
				
			||||||
 | 
							// AIX cannot preform write-lock (ie exclusive) on a
 | 
				
			||||||
 | 
							// read-only file.
 | 
				
			||||||
 | 
							flags |= os.O_RDWR
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							flags |= os.O_RDONLY
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fh, err := os.OpenFile(f.path, flags, os.FileMode(0600))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,281 @@
 | 
				
			||||||
 | 
					// Copyright 2019 Tim Heckman. All rights reserved. Use of this source code is
 | 
				
			||||||
 | 
					// governed by the BSD 3-Clause license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copyright 2018 The Go Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This code implements the filelock API using POSIX 'fcntl' locks, which attach
 | 
				
			||||||
 | 
					// to an (inode, process) pair rather than a file descriptor. To avoid unlocking
 | 
				
			||||||
 | 
					// files prematurely when the same file is opened through different descriptors,
 | 
				
			||||||
 | 
					// we allow only one read-lock at a time.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This code is adapted from the Go package:
 | 
				
			||||||
 | 
					// cmd/go/internal/lockedfile/internal/filelock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//+build aix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package flock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/sys/unix"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type lockType int16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						readLock  lockType = unix.F_RDLCK
 | 
				
			||||||
 | 
						writeLock lockType = unix.F_WRLCK
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cmdType int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						tryLock  cmdType = unix.F_SETLK
 | 
				
			||||||
 | 
						waitLock cmdType = unix.F_SETLKW
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type inode = uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type inodeLock struct {
 | 
				
			||||||
 | 
						owner *Flock
 | 
				
			||||||
 | 
						queue []<-chan *Flock
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						mu     sync.Mutex
 | 
				
			||||||
 | 
						inodes = map[*Flock]inode{}
 | 
				
			||||||
 | 
						locks  = map[inode]inodeLock{}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Lock is a blocking call to try and take an exclusive file lock. It will wait
 | 
				
			||||||
 | 
					// until it is able to obtain the exclusive file lock. It's recommended that
 | 
				
			||||||
 | 
					// TryLock() be used over this function. This function may block the ability to
 | 
				
			||||||
 | 
					// query the current Locked() or RLocked() status due to a RW-mutex lock.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If we are already exclusive-locked, this function short-circuits and returns
 | 
				
			||||||
 | 
					// immediately assuming it can take the mutex lock.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If the *Flock has a shared lock (RLock), this may transparently replace the
 | 
				
			||||||
 | 
					// shared lock with an exclusive lock on some UNIX-like operating systems. Be
 | 
				
			||||||
 | 
					// careful when using exclusive locks in conjunction with shared locks
 | 
				
			||||||
 | 
					// (RLock()), because calling Unlock() may accidentally release the exclusive
 | 
				
			||||||
 | 
					// lock that was once a shared lock.
 | 
				
			||||||
 | 
					func (f *Flock) Lock() error {
 | 
				
			||||||
 | 
						return f.lock(&f.l, writeLock)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RLock is a blocking call to try and take a shared file lock. It will wait
 | 
				
			||||||
 | 
					// until it is able to obtain the shared file lock. It's recommended that
 | 
				
			||||||
 | 
					// TryRLock() be used over this function. This function may block the ability to
 | 
				
			||||||
 | 
					// query the current Locked() or RLocked() status due to a RW-mutex lock.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If we are already shared-locked, this function short-circuits and returns
 | 
				
			||||||
 | 
					// immediately assuming it can take the mutex lock.
 | 
				
			||||||
 | 
					func (f *Flock) RLock() error {
 | 
				
			||||||
 | 
						return f.lock(&f.r, readLock)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *Flock) lock(locked *bool, flag lockType) error {
 | 
				
			||||||
 | 
						f.m.Lock()
 | 
				
			||||||
 | 
						defer f.m.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *locked {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if f.fh == nil {
 | 
				
			||||||
 | 
							if err := f.setFh(); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer f.ensureFhState()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := f.doLock(waitLock, flag, true); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*locked = true
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *Flock) doLock(cmd cmdType, lt lockType, blocking bool) (bool, error) {
 | 
				
			||||||
 | 
						// POSIX locks apply per inode and process, and the lock for an inode is
 | 
				
			||||||
 | 
						// released when *any* descriptor for that inode is closed. So we need to
 | 
				
			||||||
 | 
						// synchronize access to each inode internally, and must serialize lock and
 | 
				
			||||||
 | 
						// unlock calls that refer to the same inode through different descriptors.
 | 
				
			||||||
 | 
						fi, err := f.fh.Stat()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ino := inode(fi.Sys().(*syscall.Stat_t).Ino)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mu.Lock()
 | 
				
			||||||
 | 
						if i, dup := inodes[f]; dup && i != ino {
 | 
				
			||||||
 | 
							mu.Unlock()
 | 
				
			||||||
 | 
							return false, &os.PathError{
 | 
				
			||||||
 | 
								Path: f.Path(),
 | 
				
			||||||
 | 
								Err:  errors.New("inode for file changed since last Lock or RLock"),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inodes[f] = ino
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var wait chan *Flock
 | 
				
			||||||
 | 
						l := locks[ino]
 | 
				
			||||||
 | 
						if l.owner == f {
 | 
				
			||||||
 | 
							// This file already owns the lock, but the call may change its lock type.
 | 
				
			||||||
 | 
						} else if l.owner == nil {
 | 
				
			||||||
 | 
							// No owner: it's ours now.
 | 
				
			||||||
 | 
							l.owner = f
 | 
				
			||||||
 | 
						} else if !blocking {
 | 
				
			||||||
 | 
							// Already owned: cannot take the lock.
 | 
				
			||||||
 | 
							mu.Unlock()
 | 
				
			||||||
 | 
							return false, nil
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Already owned: add a channel to wait on.
 | 
				
			||||||
 | 
							wait = make(chan *Flock)
 | 
				
			||||||
 | 
							l.queue = append(l.queue, wait)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						locks[ino] = l
 | 
				
			||||||
 | 
						mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if wait != nil {
 | 
				
			||||||
 | 
							wait <- f
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = setlkw(f.fh.Fd(), cmd, lt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							f.doUnlock()
 | 
				
			||||||
 | 
							if cmd == tryLock && err == unix.EACCES {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *Flock) Unlock() error {
 | 
				
			||||||
 | 
						f.m.Lock()
 | 
				
			||||||
 | 
						defer f.m.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if we aren't locked or if the lockfile instance is nil
 | 
				
			||||||
 | 
						// just return a nil error because we are unlocked
 | 
				
			||||||
 | 
						if (!f.l && !f.r) || f.fh == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := f.doUnlock(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.fh.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.l = false
 | 
				
			||||||
 | 
						f.r = false
 | 
				
			||||||
 | 
						f.fh = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *Flock) doUnlock() (err error) {
 | 
				
			||||||
 | 
						var owner *Flock
 | 
				
			||||||
 | 
						mu.Lock()
 | 
				
			||||||
 | 
						ino, ok := inodes[f]
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							owner = locks[ino].owner
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if owner == f {
 | 
				
			||||||
 | 
							err = setlkw(f.fh.Fd(), waitLock, unix.F_UNLCK)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mu.Lock()
 | 
				
			||||||
 | 
						l := locks[ino]
 | 
				
			||||||
 | 
						if len(l.queue) == 0 {
 | 
				
			||||||
 | 
							// No waiters: remove the map entry.
 | 
				
			||||||
 | 
							delete(locks, ino)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// The first waiter is sending us their file now.
 | 
				
			||||||
 | 
							// Receive it and update the queue.
 | 
				
			||||||
 | 
							l.owner = <-l.queue[0]
 | 
				
			||||||
 | 
							l.queue = l.queue[1:]
 | 
				
			||||||
 | 
							locks[ino] = l
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						delete(inodes, f)
 | 
				
			||||||
 | 
						mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TryLock is the preferred function for taking an exclusive file lock. This
 | 
				
			||||||
 | 
					// function takes an RW-mutex lock before it tries to lock the file, so there is
 | 
				
			||||||
 | 
					// the possibility that this function may block for a short time if another
 | 
				
			||||||
 | 
					// goroutine is trying to take any action.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The actual file lock is non-blocking. If we are unable to get the exclusive
 | 
				
			||||||
 | 
					// file lock, the function will return false instead of waiting for the lock. If
 | 
				
			||||||
 | 
					// we get the lock, we also set the *Flock instance as being exclusive-locked.
 | 
				
			||||||
 | 
					func (f *Flock) TryLock() (bool, error) {
 | 
				
			||||||
 | 
						return f.try(&f.l, writeLock)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TryRLock is the preferred function for taking a shared file lock. This
 | 
				
			||||||
 | 
					// function takes an RW-mutex lock before it tries to lock the file, so there is
 | 
				
			||||||
 | 
					// the possibility that this function may block for a short time if another
 | 
				
			||||||
 | 
					// goroutine is trying to take any action.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The actual file lock is non-blocking. If we are unable to get the shared file
 | 
				
			||||||
 | 
					// lock, the function will return false instead of waiting for the lock. If we
 | 
				
			||||||
 | 
					// get the lock, we also set the *Flock instance as being share-locked.
 | 
				
			||||||
 | 
					func (f *Flock) TryRLock() (bool, error) {
 | 
				
			||||||
 | 
						return f.try(&f.r, readLock)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *Flock) try(locked *bool, flag lockType) (bool, error) {
 | 
				
			||||||
 | 
						f.m.Lock()
 | 
				
			||||||
 | 
						defer f.m.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *locked {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if f.fh == nil {
 | 
				
			||||||
 | 
							if err := f.setFh(); err != nil {
 | 
				
			||||||
 | 
								return false, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer f.ensureFhState()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						haslock, err := f.doLock(tryLock, flag, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*locked = haslock
 | 
				
			||||||
 | 
						return haslock, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setlkw calls FcntlFlock with cmd for the entire file indicated by fd.
 | 
				
			||||||
 | 
					func setlkw(fd uintptr, cmd cmdType, lt lockType) error {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							err := unix.FcntlFlock(fd, int(cmd), &unix.Flock_t{
 | 
				
			||||||
 | 
								Type:   int16(lt),
 | 
				
			||||||
 | 
								Whence: io.SeekStart,
 | 
				
			||||||
 | 
								Start:  0,
 | 
				
			||||||
 | 
								Len:    0, // All bytes.
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != unix.EINTR {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
// Use of this source code is governed by the BSD 3-Clause
 | 
					// Use of this source code is governed by the BSD 3-Clause
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// +build !windows
 | 
					// +build !aix,!windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package flock
 | 
					package flock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -6,6 +6,8 @@ import "github.com/gogo/protobuf/gogoproto/gogo.proto";
 | 
				
			||||||
import "google/protobuf/timestamp.proto";
 | 
					import "google/protobuf/timestamp.proto";
 | 
				
			||||||
import "github.com/moby/buildkit/solver/pb/ops.proto";
 | 
					import "github.com/moby/buildkit/solver/pb/ops.proto";
 | 
				
			||||||
import "github.com/moby/buildkit/api/types/worker.proto";
 | 
					import "github.com/moby/buildkit/api/types/worker.proto";
 | 
				
			||||||
 | 
					// import "github.com/containerd/containerd/api/types/descriptor.proto";
 | 
				
			||||||
 | 
					import "github.com/gogo/googleapis/google/rpc/status.proto";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
option (gogoproto.sizer_all) = true;
 | 
					option (gogoproto.sizer_all) = true;
 | 
				
			||||||
option (gogoproto.marshaler_all) = true;
 | 
					option (gogoproto.marshaler_all) = true;
 | 
				
			||||||
| 
						 | 
					@ -19,6 +21,8 @@ service Control {
 | 
				
			||||||
	rpc Session(stream BytesMessage) returns (stream BytesMessage);
 | 
						rpc Session(stream BytesMessage) returns (stream BytesMessage);
 | 
				
			||||||
	rpc ListWorkers(ListWorkersRequest) returns (ListWorkersResponse);
 | 
						rpc ListWorkers(ListWorkersRequest) returns (ListWorkersResponse);
 | 
				
			||||||
	rpc Info(InfoRequest) returns (InfoResponse);
 | 
						rpc Info(InfoRequest) returns (InfoResponse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rpc ListenBuildHistory(BuildHistoryRequest) returns (stream BuildHistoryEvent);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message PruneRequest {
 | 
					message PruneRequest {
 | 
				
			||||||
| 
						 | 
					@ -163,3 +167,56 @@ message InfoRequest {}
 | 
				
			||||||
message InfoResponse {
 | 
					message InfoResponse {
 | 
				
			||||||
	moby.buildkit.v1.types.BuildkitVersion buildkitVersion = 1;
 | 
						moby.buildkit.v1.types.BuildkitVersion buildkitVersion = 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message BuildHistoryRequest {
 | 
				
			||||||
 | 
						bool ActiveOnly = 1;
 | 
				
			||||||
 | 
						string Ref = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum BuildHistoryEventType {
 | 
				
			||||||
 | 
						STARTED = 0;
 | 
				
			||||||
 | 
						COMPLETE = 1;
 | 
				
			||||||
 | 
						DELETED = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message BuildHistoryEvent {
 | 
				
			||||||
 | 
						BuildHistoryEventType type = 1;
 | 
				
			||||||
 | 
						BuildHistoryRecord record = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message BuildHistoryRecord {
 | 
				
			||||||
 | 
						string Ref = 1;
 | 
				
			||||||
 | 
						string Frontend = 2;
 | 
				
			||||||
 | 
						map<string, string> FrontendAttrs = 3;
 | 
				
			||||||
 | 
						repeated Exporter Exporters = 4;
 | 
				
			||||||
 | 
						google.rpc.Status error = 5;
 | 
				
			||||||
 | 
						google.protobuf.Timestamp CreatedAt = 6 [(gogoproto.stdtime) = true];
 | 
				
			||||||
 | 
						google.protobuf.Timestamp CompletedAt = 7 [(gogoproto.stdtime) = true];
 | 
				
			||||||
 | 
						Descriptor logs = 8;
 | 
				
			||||||
 | 
						map<string, string> ExporterResponse = 9;
 | 
				
			||||||
 | 
						BuildResultInfo Result = 10;
 | 
				
			||||||
 | 
						map<string, BuildResultInfo> Results = 11;
 | 
				
			||||||
 | 
						int32 Generation = 12;
 | 
				
			||||||
 | 
						// TODO: tags
 | 
				
			||||||
 | 
						// TODO: steps/cache summary
 | 
				
			||||||
 | 
						// TODO: unclipped logs
 | 
				
			||||||
 | 
						// TODO: pinning
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message Descriptor {
 | 
				
			||||||
 | 
						string media_type = 1;
 | 
				
			||||||
 | 
						string digest = 2 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
 | 
				
			||||||
 | 
						int64 size = 3;
 | 
				
			||||||
 | 
						map<string, string> annotations = 5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message BuildResultInfo {
 | 
				
			||||||
 | 
						Descriptor Result = 1;
 | 
				
			||||||
 | 
						repeated Descriptor Attestations = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message Exporter {
 | 
				
			||||||
 | 
						string Type = 1;
 | 
				
			||||||
 | 
						map<string, string> Attrs = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -168,12 +168,12 @@ func (c *Client) setupDelegatedTracing(ctx context.Context, td TracerDelegate) e
 | 
				
			||||||
	return td.SetSpanExporter(ctx, e)
 | 
						return td.SetSpanExporter(ctx, e)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) controlClient() controlapi.ControlClient {
 | 
					func (c *Client) ControlClient() controlapi.ControlClient {
 | 
				
			||||||
	return controlapi.NewControlClient(c.conn)
 | 
						return controlapi.NewControlClient(c.conn)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) Dialer() session.Dialer {
 | 
					func (c *Client) Dialer() session.Dialer {
 | 
				
			||||||
	return grpchijack.Dialer(c.controlClient())
 | 
						return grpchijack.Dialer(c.ControlClient())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) Close() error {
 | 
					func (c *Client) Close() error {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ func (c *Client) DiskUsage(ctx context.Context, opts ...DiskUsageOption) ([]*Usa
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := &controlapi.DiskUsageRequest{Filter: info.Filter}
 | 
						req := &controlapi.DiskUsageRequest{Filter: info.Filter}
 | 
				
			||||||
	resp, err := c.controlClient().DiskUsage(ctx, req)
 | 
						resp, err := c.ControlClient().DiskUsage(ctx, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrap(err, "failed to call diskusage")
 | 
							return nil, errors.Wrap(err, "failed to call diskusage")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ type BuildkitVersion struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) Info(ctx context.Context) (*Info, error) {
 | 
					func (c *Client) Info(ctx context.Context) (*Info, error) {
 | 
				
			||||||
	res, err := c.controlClient().Info(ctx, &controlapi.InfoRequest{})
 | 
						res, err := c.ControlClient().Info(ctx, &controlapi.InfoRequest{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrap(err, "failed to call info")
 | 
							return nil, errors.Wrap(err, "failed to call info")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -455,7 +455,7 @@ func Differ(t DiffType, required bool) LocalOption {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func OCILayout(contentStoreID string, dig digest.Digest, opts ...OCILayoutOption) State {
 | 
					func OCILayout(store string, digest digest.Digest, opts ...OCILayoutOption) State {
 | 
				
			||||||
	gi := &OCILayoutInfo{}
 | 
						gi := &OCILayoutInfo{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, o := range opts {
 | 
						for _, o := range opts {
 | 
				
			||||||
| 
						 | 
					@ -474,7 +474,7 @@ func OCILayout(contentStoreID string, dig digest.Digest, opts ...OCILayoutOption
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	addCap(&gi.Constraints, pb.CapSourceOCILayout)
 | 
						addCap(&gi.Constraints, pb.CapSourceOCILayout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	source := NewSource(fmt.Sprintf("oci-layout://%s@%s", contentStoreID, dig), attrs, gi.Constraints)
 | 
						source := NewSource(fmt.Sprintf("oci-layout://%s@%s", store, digest), attrs, gi.Constraints)
 | 
				
			||||||
	return NewState(source.Output())
 | 
						return NewState(source.Output())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ func (c *Client) Prune(ctx context.Context, ch chan UsageInfo, opts ...PruneOpti
 | 
				
			||||||
	if info.All {
 | 
						if info.All {
 | 
				
			||||||
		req.All = true
 | 
							req.All = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cl, err := c.controlClient().Prune(ctx, req)
 | 
						cl, err := c.ControlClient().Prune(ctx, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.Wrap(err, "failed to call prune")
 | 
							return errors.Wrap(err, "failed to call prune")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -204,7 +204,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
 | 
				
			||||||
		eg.Go(func() error {
 | 
							eg.Go(func() error {
 | 
				
			||||||
			sd := c.sessionDialer
 | 
								sd := c.sessionDialer
 | 
				
			||||||
			if sd == nil {
 | 
								if sd == nil {
 | 
				
			||||||
				sd = grpchijack.Dialer(c.controlClient())
 | 
									sd = grpchijack.Dialer(c.ControlClient())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return s.Run(statusContext, sd)
 | 
								return s.Run(statusContext, sd)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
| 
						 | 
					@ -247,7 +247,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
 | 
				
			||||||
			frontendInputs[key] = def.ToPB()
 | 
								frontendInputs[key] = def.ToPB()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		resp, err := c.controlClient().Solve(ctx, &controlapi.SolveRequest{
 | 
							resp, err := c.ControlClient().Solve(ctx, &controlapi.SolveRequest{
 | 
				
			||||||
			Ref:            ref,
 | 
								Ref:            ref,
 | 
				
			||||||
			Definition:     pbd,
 | 
								Definition:     pbd,
 | 
				
			||||||
			Exporter:       ex.Type,
 | 
								Exporter:       ex.Type,
 | 
				
			||||||
| 
						 | 
					@ -291,7 +291,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	eg.Go(func() error {
 | 
						eg.Go(func() error {
 | 
				
			||||||
		stream, err := c.controlClient().Status(statusContext, &controlapi.StatusRequest{
 | 
							stream, err := c.ControlClient().Status(statusContext, &controlapi.StatusRequest{
 | 
				
			||||||
			Ref: ref,
 | 
								Ref: ref,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -305,52 +305,8 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				return errors.Wrap(err, "failed to receive status")
 | 
									return errors.Wrap(err, "failed to receive status")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			s := SolveStatus{}
 | 
					 | 
				
			||||||
			for _, v := range resp.Vertexes {
 | 
					 | 
				
			||||||
				s.Vertexes = append(s.Vertexes, &Vertex{
 | 
					 | 
				
			||||||
					Digest:        v.Digest,
 | 
					 | 
				
			||||||
					Inputs:        v.Inputs,
 | 
					 | 
				
			||||||
					Name:          v.Name,
 | 
					 | 
				
			||||||
					Started:       v.Started,
 | 
					 | 
				
			||||||
					Completed:     v.Completed,
 | 
					 | 
				
			||||||
					Error:         v.Error,
 | 
					 | 
				
			||||||
					Cached:        v.Cached,
 | 
					 | 
				
			||||||
					ProgressGroup: v.ProgressGroup,
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			for _, v := range resp.Statuses {
 | 
					 | 
				
			||||||
				s.Statuses = append(s.Statuses, &VertexStatus{
 | 
					 | 
				
			||||||
					ID:        v.ID,
 | 
					 | 
				
			||||||
					Vertex:    v.Vertex,
 | 
					 | 
				
			||||||
					Name:      v.Name,
 | 
					 | 
				
			||||||
					Total:     v.Total,
 | 
					 | 
				
			||||||
					Current:   v.Current,
 | 
					 | 
				
			||||||
					Timestamp: v.Timestamp,
 | 
					 | 
				
			||||||
					Started:   v.Started,
 | 
					 | 
				
			||||||
					Completed: v.Completed,
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			for _, v := range resp.Logs {
 | 
					 | 
				
			||||||
				s.Logs = append(s.Logs, &VertexLog{
 | 
					 | 
				
			||||||
					Vertex:    v.Vertex,
 | 
					 | 
				
			||||||
					Stream:    int(v.Stream),
 | 
					 | 
				
			||||||
					Data:      v.Msg,
 | 
					 | 
				
			||||||
					Timestamp: v.Timestamp,
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			for _, v := range resp.Warnings {
 | 
					 | 
				
			||||||
				s.Warnings = append(s.Warnings, &VertexWarning{
 | 
					 | 
				
			||||||
					Vertex:     v.Vertex,
 | 
					 | 
				
			||||||
					Level:      int(v.Level),
 | 
					 | 
				
			||||||
					Short:      v.Short,
 | 
					 | 
				
			||||||
					Detail:     v.Detail,
 | 
					 | 
				
			||||||
					URL:        v.Url,
 | 
					 | 
				
			||||||
					SourceInfo: v.Info,
 | 
					 | 
				
			||||||
					Range:      v.Ranges,
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if statusChan != nil {
 | 
								if statusChan != nil {
 | 
				
			||||||
				statusChan <- &s
 | 
									statusChan <- NewSolveStatus(resp)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
| 
						 | 
					@ -393,6 +349,54 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
 | 
				
			||||||
	return res, nil
 | 
						return res, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSolveStatus(resp *controlapi.StatusResponse) *SolveStatus {
 | 
				
			||||||
 | 
						s := &SolveStatus{}
 | 
				
			||||||
 | 
						for _, v := range resp.Vertexes {
 | 
				
			||||||
 | 
							s.Vertexes = append(s.Vertexes, &Vertex{
 | 
				
			||||||
 | 
								Digest:        v.Digest,
 | 
				
			||||||
 | 
								Inputs:        v.Inputs,
 | 
				
			||||||
 | 
								Name:          v.Name,
 | 
				
			||||||
 | 
								Started:       v.Started,
 | 
				
			||||||
 | 
								Completed:     v.Completed,
 | 
				
			||||||
 | 
								Error:         v.Error,
 | 
				
			||||||
 | 
								Cached:        v.Cached,
 | 
				
			||||||
 | 
								ProgressGroup: v.ProgressGroup,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, v := range resp.Statuses {
 | 
				
			||||||
 | 
							s.Statuses = append(s.Statuses, &VertexStatus{
 | 
				
			||||||
 | 
								ID:        v.ID,
 | 
				
			||||||
 | 
								Vertex:    v.Vertex,
 | 
				
			||||||
 | 
								Name:      v.Name,
 | 
				
			||||||
 | 
								Total:     v.Total,
 | 
				
			||||||
 | 
								Current:   v.Current,
 | 
				
			||||||
 | 
								Timestamp: v.Timestamp,
 | 
				
			||||||
 | 
								Started:   v.Started,
 | 
				
			||||||
 | 
								Completed: v.Completed,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, v := range resp.Logs {
 | 
				
			||||||
 | 
							s.Logs = append(s.Logs, &VertexLog{
 | 
				
			||||||
 | 
								Vertex:    v.Vertex,
 | 
				
			||||||
 | 
								Stream:    int(v.Stream),
 | 
				
			||||||
 | 
								Data:      v.Msg,
 | 
				
			||||||
 | 
								Timestamp: v.Timestamp,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, v := range resp.Warnings {
 | 
				
			||||||
 | 
							s.Warnings = append(s.Warnings, &VertexWarning{
 | 
				
			||||||
 | 
								Vertex:     v.Vertex,
 | 
				
			||||||
 | 
								Level:      int(v.Level),
 | 
				
			||||||
 | 
								Short:      v.Short,
 | 
				
			||||||
 | 
								Detail:     v.Detail,
 | 
				
			||||||
 | 
								URL:        v.Url,
 | 
				
			||||||
 | 
								SourceInfo: v.Info,
 | 
				
			||||||
 | 
								Range:      v.Ranges,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func prepareSyncedDirs(def *llb.Definition, localDirs map[string]string) (filesync.StaticDirSource, error) {
 | 
					func prepareSyncedDirs(def *llb.Definition, localDirs map[string]string) (filesync.StaticDirSource, error) {
 | 
				
			||||||
	for _, d := range localDirs {
 | 
						for _, d := range localDirs {
 | 
				
			||||||
		fi, err := os.Stat(d)
 | 
							fi, err := os.Stat(d)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@ func (c *Client) ListWorkers(ctx context.Context, opts ...ListWorkersOption) ([]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := &controlapi.ListWorkersRequest{Filter: info.Filter}
 | 
						req := &controlapi.ListWorkersRequest{Filter: info.Filter}
 | 
				
			||||||
	resp, err := c.controlClient().ListWorkers(ctx, req)
 | 
						resp, err := c.ControlClient().ListWorkers(ctx, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrap(err, "failed to list workers")
 | 
							return nil, errors.Wrap(err, "failed to list workers")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,8 +13,7 @@ const (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	// TODO: update this before next buildkit release
 | 
						defaultSBOMGenerator = "docker/buildkit-syft-scanner:stable-1"
 | 
				
			||||||
	defaultSBOMGenerator = "jedevc/buildkit-syft-scanner:master@sha256:de630f621eb0ab1bb1245cea76d01c5bddfe78af4f5b9adecde424cb7ec5605e"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Filter(v map[string]string) map[string]string {
 | 
					func Filter(v map[string]string) map[string]string {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,9 +3,14 @@ package client
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	pb "github.com/moby/buildkit/frontend/gateway/pb"
 | 
						pb "github.com/moby/buildkit/frontend/gateway/pb"
 | 
				
			||||||
	"github.com/moby/buildkit/solver/result"
 | 
						"github.com/moby/buildkit/solver/result"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func AttestationToPB(a *result.Attestation) (*pb.Attestation, error) {
 | 
					func AttestationToPB(a *result.Attestation) (*pb.Attestation, error) {
 | 
				
			||||||
 | 
						if a.ContentFunc != nil {
 | 
				
			||||||
 | 
							return nil, errors.Errorf("attestation callback cannot be sent through gateway")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	subjects := make([]*pb.InTotoSubject, len(a.InToto.Subjects))
 | 
						subjects := make([]*pb.InTotoSubject, len(a.InToto.Subjects))
 | 
				
			||||||
	for i, subject := range a.InToto.Subjects {
 | 
						for i, subject := range a.InToto.Subjects {
 | 
				
			||||||
		subjects[i] = &pb.InTotoSubject{
 | 
							subjects[i] = &pb.InTotoSubject{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,10 @@ package identity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	cryptorand "crypto/rand"
 | 
						cryptorand "crypto/rand"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"math/big"
 | 
						"math/big"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
| 
						 | 
					@ -45,7 +46,7 @@ func NewID() string {
 | 
				
			||||||
	var p [randomIDEntropyBytes]byte
 | 
						var p [randomIDEntropyBytes]byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := io.ReadFull(idReader, p[:]); err != nil {
 | 
						if _, err := io.ReadFull(idReader, p[:]); err != nil {
 | 
				
			||||||
		panic(fmt.Errorf("failed to read random bytes: %v", err))
 | 
							panic(errors.Wrap(err, "failed to read random bytes: %v"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p[0] |= 0x80 // set high bit to avoid the need for padding
 | 
						p[0] |= 0x80 // set high bit to avoid the need for padding
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
package pb
 | 
					package pb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:generate protoc -I=. -I=../../vendor/ --gogofaster_out=. ops.proto
 | 
					//go:generate protoc -I=. -I=../../vendor/ -I=../../vendor/github.com/gogo/protobuf/ --gogofaster_out=. ops.proto
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,11 +8,11 @@ import (
 | 
				
			||||||
type Attestation struct {
 | 
					type Attestation struct {
 | 
				
			||||||
	Kind pb.AttestationKind
 | 
						Kind pb.AttestationKind
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Ref  string
 | 
						Ref         string
 | 
				
			||||||
	Path string
 | 
						Path        string
 | 
				
			||||||
 | 
					 | 
				
			||||||
	InToto      InTotoAttestation
 | 
					 | 
				
			||||||
	ContentFunc func() ([]byte, error)
 | 
						ContentFunc func() ([]byte, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						InToto InTotoAttestation
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type InTotoAttestation struct {
 | 
					type InTotoAttestation struct {
 | 
				
			||||||
| 
						 | 
					@ -27,10 +27,18 @@ type InTotoSubject struct {
 | 
				
			||||||
	Digest []digest.Digest
 | 
						Digest []digest.Digest
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func DigestMap(ds ...digest.Digest) map[string]string {
 | 
					func ToDigestMap(ds ...digest.Digest) map[string]string {
 | 
				
			||||||
	m := map[string]string{}
 | 
						m := map[string]string{}
 | 
				
			||||||
	for _, d := range ds {
 | 
						for _, d := range ds {
 | 
				
			||||||
		m[d.Algorithm().String()] = d.Encoded()
 | 
							m[d.Algorithm().String()] = d.Encoded()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return m
 | 
						return m
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func FromDigestMap(m map[string]string) []digest.Digest {
 | 
				
			||||||
 | 
						var ds []digest.Digest
 | 
				
			||||||
 | 
						for k, v := range m {
 | 
				
			||||||
 | 
							ds = append(ds, digest.NewDigestFromEncoded(digest.Algorithm(k), v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ds
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,16 +41,18 @@ func (r *Result[T]) AddRef(k string, ref T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Result[T]) AddAttestation(k string, v Attestation, ref T) {
 | 
					func (r *Result[T]) AddAttestation(k string, v Attestation, ref T) {
 | 
				
			||||||
	r.mu.Lock()
 | 
						r.mu.Lock()
 | 
				
			||||||
	if r.Refs == nil {
 | 
						if reflect.ValueOf(ref).IsValid() {
 | 
				
			||||||
		r.Refs = map[string]T{}
 | 
							if r.Refs == nil {
 | 
				
			||||||
 | 
								r.Refs = map[string]T{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !strings.HasPrefix(v.Ref, attestationRefPrefix) {
 | 
				
			||||||
 | 
								v.Ref = attestationRefPrefix + identity.NewID()
 | 
				
			||||||
 | 
								r.Refs[v.Ref] = ref
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if r.Attestations == nil {
 | 
						if r.Attestations == nil {
 | 
				
			||||||
		r.Attestations = map[string][]Attestation{}
 | 
							r.Attestations = map[string][]Attestation{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if v.ContentFunc == nil && !strings.HasPrefix(v.Ref, attestationRefPrefix) {
 | 
					 | 
				
			||||||
		v.Ref = "attestation:" + identity.NewID()
 | 
					 | 
				
			||||||
		r.Refs[v.Ref] = ref
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	r.Attestations[k] = append(r.Attestations[k], v)
 | 
						r.Attestations[k] = append(r.Attestations[k], v)
 | 
				
			||||||
	r.mu.Unlock()
 | 
						r.mu.Unlock()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/containerd/typeurl"
 | 
						"github.com/containerd/typeurl"
 | 
				
			||||||
 | 
						rpc "github.com/gogo/googleapis/google/rpc"
 | 
				
			||||||
	gogotypes "github.com/gogo/protobuf/types"
 | 
						gogotypes "github.com/gogo/protobuf/types"
 | 
				
			||||||
	"github.com/golang/protobuf/proto" //nolint:staticcheck
 | 
						"github.com/golang/protobuf/proto" //nolint:staticcheck
 | 
				
			||||||
	"github.com/golang/protobuf/ptypes/any"
 | 
						"github.com/golang/protobuf/ptypes/any"
 | 
				
			||||||
| 
						 | 
					@ -181,7 +182,7 @@ func FromGRPC(err error) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, s := range stacks {
 | 
						for _, s := range stacks {
 | 
				
			||||||
		if s != nil {
 | 
							if s != nil {
 | 
				
			||||||
			err = stack.Wrap(err, *s)
 | 
								err = stack.Wrap(err, s)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -196,6 +197,20 @@ func FromGRPC(err error) error {
 | 
				
			||||||
	return stack.Enable(err)
 | 
						return stack.Enable(err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ToRPCStatus(st *spb.Status) *rpc.Status {
 | 
				
			||||||
 | 
						details := make([]*gogotypes.Any, len(st.Details))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, d := range st.Details {
 | 
				
			||||||
 | 
							details[i] = gogoAny(d)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &rpc.Status{
 | 
				
			||||||
 | 
							Code:    int32(st.Code),
 | 
				
			||||||
 | 
							Message: st.Message,
 | 
				
			||||||
 | 
							Details: details,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type grpcStatusError struct {
 | 
					type grpcStatusError struct {
 | 
				
			||||||
	st *status.Status
 | 
						st *status.Status
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ type MultiReader struct {
 | 
				
			||||||
	initialized bool
 | 
						initialized bool
 | 
				
			||||||
	done        chan struct{}
 | 
						done        chan struct{}
 | 
				
			||||||
	writers     map[*progressWriter]func()
 | 
						writers     map[*progressWriter]func()
 | 
				
			||||||
 | 
						sent        []*Progress
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewMultiReader(pr Reader) *MultiReader {
 | 
					func NewMultiReader(pr Reader) *MultiReader {
 | 
				
			||||||
| 
						 | 
					@ -31,9 +32,59 @@ func (mr *MultiReader) Reader(ctx context.Context) Reader {
 | 
				
			||||||
	pw, _, ctx := NewFromContext(ctx)
 | 
						pw, _, ctx := NewFromContext(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	w := pw.(*progressWriter)
 | 
						w := pw.(*progressWriter)
 | 
				
			||||||
	mr.writers[w] = closeWriter
 | 
					
 | 
				
			||||||
 | 
						isBehind := len(mr.sent) > 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !isBehind {
 | 
				
			||||||
 | 
							mr.writers[w] = closeWriter
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
 | 
							if isBehind {
 | 
				
			||||||
 | 
								close := func() {
 | 
				
			||||||
 | 
									w.Close()
 | 
				
			||||||
 | 
									closeWriter()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								i := 0
 | 
				
			||||||
 | 
								for {
 | 
				
			||||||
 | 
									mr.mu.Lock()
 | 
				
			||||||
 | 
									sent := mr.sent
 | 
				
			||||||
 | 
									count := len(sent) - i
 | 
				
			||||||
 | 
									if count == 0 {
 | 
				
			||||||
 | 
										select {
 | 
				
			||||||
 | 
										case <-ctx.Done():
 | 
				
			||||||
 | 
											close()
 | 
				
			||||||
 | 
											mr.mu.Unlock()
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										case <-mr.done:
 | 
				
			||||||
 | 
											close()
 | 
				
			||||||
 | 
											mr.mu.Unlock()
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										default:
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										mr.writers[w] = closeWriter
 | 
				
			||||||
 | 
										mr.mu.Unlock()
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									mr.mu.Unlock()
 | 
				
			||||||
 | 
									for i, p := range sent[i:] {
 | 
				
			||||||
 | 
										w.writeRawProgress(p)
 | 
				
			||||||
 | 
										if i%100 == 0 {
 | 
				
			||||||
 | 
											select {
 | 
				
			||||||
 | 
											case <-ctx.Done():
 | 
				
			||||||
 | 
												close()
 | 
				
			||||||
 | 
												return
 | 
				
			||||||
 | 
											case <-mr.done:
 | 
				
			||||||
 | 
												close()
 | 
				
			||||||
 | 
												return
 | 
				
			||||||
 | 
											default:
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									i += count
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case <-ctx.Done():
 | 
							case <-ctx.Done():
 | 
				
			||||||
		case <-mr.done:
 | 
							case <-mr.done:
 | 
				
			||||||
| 
						 | 
					@ -61,6 +112,7 @@ func (mr *MultiReader) handle() error {
 | 
				
			||||||
					w.Close()
 | 
										w.Close()
 | 
				
			||||||
					c()
 | 
										c()
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									close(mr.done)
 | 
				
			||||||
				mr.mu.Unlock()
 | 
									mr.mu.Unlock()
 | 
				
			||||||
				return nil
 | 
									return nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -72,6 +124,7 @@ func (mr *MultiReader) handle() error {
 | 
				
			||||||
				w.writeRawProgress(p)
 | 
									w.writeRawProgress(p)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							mr.sent = append(mr.sent, p...)
 | 
				
			||||||
		mr.mu.Unlock()
 | 
							mr.mu.Unlock()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
package sshutil
 | 
					package sshutil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
| 
						 | 
					@ -11,7 +12,7 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultPort = 22
 | 
					const defaultPort = 22
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var errCallbackDone = fmt.Errorf("callback failed on purpose")
 | 
					var errCallbackDone = errors.New("callback failed on purpose")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// addDefaultPort appends a default port if hostport doesn't contain one
 | 
					// addDefaultPort appends a default port if hostport doesn't contain one
 | 
				
			||||||
func addDefaultPort(hostport string, defaultPort int) string {
 | 
					func addDefaultPort(hostport string, defaultPort int) string {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ func Enable(err error) error {
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Wrap(err error, s Stack) error {
 | 
					func Wrap(err error, s *Stack) error {
 | 
				
			||||||
	return &withStack{stack: s, error: err}
 | 
						return &withStack{stack: s, error: err}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,7 +169,7 @@ func convertStack(s errors.StackTrace) *Stack {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type withStack struct {
 | 
					type withStack struct {
 | 
				
			||||||
	stack Stack
 | 
						stack *Stack
 | 
				
			||||||
	error
 | 
						error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -178,5 +178,5 @@ func (e *withStack) Unwrap() error {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (e *withStack) StackTrace() *Stack {
 | 
					func (e *withStack) StackTrace() *Stack {
 | 
				
			||||||
	return &e.stack
 | 
						return e.stack
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,172 +1,261 @@
 | 
				
			||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
					// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
				
			||||||
 | 
					// versions:
 | 
				
			||||||
 | 
					// 	protoc-gen-go v1.28.1
 | 
				
			||||||
 | 
					// 	protoc        v3.11.4
 | 
				
			||||||
// source: stack.proto
 | 
					// source: stack.proto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package stack
 | 
					package stack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	fmt "fmt"
 | 
						protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 | 
				
			||||||
	proto "github.com/golang/protobuf/proto"
 | 
						protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 | 
				
			||||||
	math "math"
 | 
						reflect "reflect"
 | 
				
			||||||
 | 
						sync "sync"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Reference imports to suppress errors if they are not otherwise used.
 | 
					const (
 | 
				
			||||||
var _ = proto.Marshal
 | 
						// Verify that this generated code is sufficiently up-to-date.
 | 
				
			||||||
var _ = fmt.Errorf
 | 
						_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
 | 
				
			||||||
var _ = math.Inf
 | 
						// Verify that runtime/protoimpl is sufficiently up-to-date.
 | 
				
			||||||
 | 
						_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 | 
				
			||||||
// This is a compile-time assertion to ensure that this generated file
 | 
					)
 | 
				
			||||||
// is compatible with the proto package it is being compiled against.
 | 
					 | 
				
			||||||
// A compilation error at this line likely means your copy of the
 | 
					 | 
				
			||||||
// proto package needs to be updated.
 | 
					 | 
				
			||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Stack struct {
 | 
					type Stack struct {
 | 
				
			||||||
	Frames               []*Frame `protobuf:"bytes,1,rep,name=frames,proto3" json:"frames,omitempty"`
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
	Cmdline              []string `protobuf:"bytes,2,rep,name=cmdline,proto3" json:"cmdline,omitempty"`
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
	Pid                  int32    `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"`
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
	Version              string   `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"`
 | 
					
 | 
				
			||||||
	Revision             string   `protobuf:"bytes,5,opt,name=revision,proto3" json:"revision,omitempty"`
 | 
						Frames   []*Frame `protobuf:"bytes,1,rep,name=frames,proto3" json:"frames,omitempty"`
 | 
				
			||||||
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 | 
						Cmdline  []string `protobuf:"bytes,2,rep,name=cmdline,proto3" json:"cmdline,omitempty"`
 | 
				
			||||||
	XXX_unrecognized     []byte   `json:"-"`
 | 
						Pid      int32    `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"`
 | 
				
			||||||
	XXX_sizecache        int32    `json:"-"`
 | 
						Version  string   `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"`
 | 
				
			||||||
 | 
						Revision string   `protobuf:"bytes,5,opt,name=revision,proto3" json:"revision,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Stack) Reset()         { *m = Stack{} }
 | 
					func (x *Stack) Reset() {
 | 
				
			||||||
func (m *Stack) String() string { return proto.CompactTextString(m) }
 | 
						*x = Stack{}
 | 
				
			||||||
func (*Stack) ProtoMessage()    {}
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_stack_proto_msgTypes[0]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Stack) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*Stack) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Stack) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_stack_proto_msgTypes[0]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use Stack.ProtoReflect.Descriptor instead.
 | 
				
			||||||
func (*Stack) Descriptor() ([]byte, []int) {
 | 
					func (*Stack) Descriptor() ([]byte, []int) {
 | 
				
			||||||
	return fileDescriptor_b44c07feb2ca0a5a, []int{0}
 | 
						return file_stack_proto_rawDescGZIP(), []int{0}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Stack) XXX_Unmarshal(b []byte) error {
 | 
					func (x *Stack) GetFrames() []*Frame {
 | 
				
			||||||
	return xxx_messageInfo_Stack.Unmarshal(m, b)
 | 
						if x != nil {
 | 
				
			||||||
}
 | 
							return x.Frames
 | 
				
			||||||
func (m *Stack) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 | 
					 | 
				
			||||||
	return xxx_messageInfo_Stack.Marshal(b, m, deterministic)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func (m *Stack) XXX_Merge(src proto.Message) {
 | 
					 | 
				
			||||||
	xxx_messageInfo_Stack.Merge(m, src)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func (m *Stack) XXX_Size() int {
 | 
					 | 
				
			||||||
	return xxx_messageInfo_Stack.Size(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func (m *Stack) XXX_DiscardUnknown() {
 | 
					 | 
				
			||||||
	xxx_messageInfo_Stack.DiscardUnknown(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var xxx_messageInfo_Stack proto.InternalMessageInfo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (m *Stack) GetFrames() []*Frame {
 | 
					 | 
				
			||||||
	if m != nil {
 | 
					 | 
				
			||||||
		return m.Frames
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Stack) GetCmdline() []string {
 | 
					func (x *Stack) GetCmdline() []string {
 | 
				
			||||||
	if m != nil {
 | 
						if x != nil {
 | 
				
			||||||
		return m.Cmdline
 | 
							return x.Cmdline
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Stack) GetPid() int32 {
 | 
					func (x *Stack) GetPid() int32 {
 | 
				
			||||||
	if m != nil {
 | 
						if x != nil {
 | 
				
			||||||
		return m.Pid
 | 
							return x.Pid
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0
 | 
						return 0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Stack) GetVersion() string {
 | 
					func (x *Stack) GetVersion() string {
 | 
				
			||||||
	if m != nil {
 | 
						if x != nil {
 | 
				
			||||||
		return m.Version
 | 
							return x.Version
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Stack) GetRevision() string {
 | 
					func (x *Stack) GetRevision() string {
 | 
				
			||||||
	if m != nil {
 | 
						if x != nil {
 | 
				
			||||||
		return m.Revision
 | 
							return x.Revision
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Frame struct {
 | 
					type Frame struct {
 | 
				
			||||||
	Name                 string   `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
	File                 string   `protobuf:"bytes,2,opt,name=File,proto3" json:"File,omitempty"`
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
	Line                 int32    `protobuf:"varint,3,opt,name=Line,proto3" json:"Line,omitempty"`
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 | 
					
 | 
				
			||||||
	XXX_unrecognized     []byte   `json:"-"`
 | 
						Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
 | 
				
			||||||
	XXX_sizecache        int32    `json:"-"`
 | 
						File string `protobuf:"bytes,2,opt,name=File,proto3" json:"File,omitempty"`
 | 
				
			||||||
 | 
						Line int32  `protobuf:"varint,3,opt,name=Line,proto3" json:"Line,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Frame) Reset()         { *m = Frame{} }
 | 
					func (x *Frame) Reset() {
 | 
				
			||||||
func (m *Frame) String() string { return proto.CompactTextString(m) }
 | 
						*x = Frame{}
 | 
				
			||||||
func (*Frame) ProtoMessage()    {}
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_stack_proto_msgTypes[1]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Frame) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*Frame) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Frame) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_stack_proto_msgTypes[1]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use Frame.ProtoReflect.Descriptor instead.
 | 
				
			||||||
func (*Frame) Descriptor() ([]byte, []int) {
 | 
					func (*Frame) Descriptor() ([]byte, []int) {
 | 
				
			||||||
	return fileDescriptor_b44c07feb2ca0a5a, []int{1}
 | 
						return file_stack_proto_rawDescGZIP(), []int{1}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Frame) XXX_Unmarshal(b []byte) error {
 | 
					func (x *Frame) GetName() string {
 | 
				
			||||||
	return xxx_messageInfo_Frame.Unmarshal(m, b)
 | 
						if x != nil {
 | 
				
			||||||
}
 | 
							return x.Name
 | 
				
			||||||
func (m *Frame) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 | 
					 | 
				
			||||||
	return xxx_messageInfo_Frame.Marshal(b, m, deterministic)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func (m *Frame) XXX_Merge(src proto.Message) {
 | 
					 | 
				
			||||||
	xxx_messageInfo_Frame.Merge(m, src)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func (m *Frame) XXX_Size() int {
 | 
					 | 
				
			||||||
	return xxx_messageInfo_Frame.Size(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func (m *Frame) XXX_DiscardUnknown() {
 | 
					 | 
				
			||||||
	xxx_messageInfo_Frame.DiscardUnknown(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var xxx_messageInfo_Frame proto.InternalMessageInfo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (m *Frame) GetName() string {
 | 
					 | 
				
			||||||
	if m != nil {
 | 
					 | 
				
			||||||
		return m.Name
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Frame) GetFile() string {
 | 
					func (x *Frame) GetFile() string {
 | 
				
			||||||
	if m != nil {
 | 
						if x != nil {
 | 
				
			||||||
		return m.File
 | 
							return x.File
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Frame) GetLine() int32 {
 | 
					func (x *Frame) GetLine() int32 {
 | 
				
			||||||
	if m != nil {
 | 
						if x != nil {
 | 
				
			||||||
		return m.Line
 | 
							return x.Line
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0
 | 
						return 0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					var File_stack_proto protoreflect.FileDescriptor
 | 
				
			||||||
	proto.RegisterType((*Stack)(nil), "stack.Stack")
 | 
					
 | 
				
			||||||
	proto.RegisterType((*Frame)(nil), "stack.Frame")
 | 
					var file_stack_proto_rawDesc = []byte{
 | 
				
			||||||
 | 
						0x0a, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x73,
 | 
				
			||||||
 | 
						0x74, 0x61, 0x63, 0x6b, 0x22, 0x8f, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x24,
 | 
				
			||||||
 | 
						0x0a, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c,
 | 
				
			||||||
 | 
						0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x06, 0x66, 0x72,
 | 
				
			||||||
 | 
						0x61, 0x6d, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18,
 | 
				
			||||||
 | 
						0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x10,
 | 
				
			||||||
 | 
						0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64,
 | 
				
			||||||
 | 
						0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65,
 | 
				
			||||||
 | 
						0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65,
 | 
				
			||||||
 | 
						0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x05, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12,
 | 
				
			||||||
 | 
						0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e,
 | 
				
			||||||
 | 
						0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x18,
 | 
				
			||||||
 | 
						0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f,
 | 
				
			||||||
 | 
						0x74, 0x6f, 0x33,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					var (
 | 
				
			||||||
	proto.RegisterFile("stack.proto", fileDescriptor_b44c07feb2ca0a5a)
 | 
						file_stack_proto_rawDescOnce sync.Once
 | 
				
			||||||
 | 
						file_stack_proto_rawDescData = file_stack_proto_rawDesc
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func file_stack_proto_rawDescGZIP() []byte {
 | 
				
			||||||
 | 
						file_stack_proto_rawDescOnce.Do(func() {
 | 
				
			||||||
 | 
							file_stack_proto_rawDescData = protoimpl.X.CompressGZIP(file_stack_proto_rawDescData)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return file_stack_proto_rawDescData
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var fileDescriptor_b44c07feb2ca0a5a = []byte{
 | 
					var file_stack_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 | 
				
			||||||
	// 185 bytes of a gzipped FileDescriptorProto
 | 
					var file_stack_proto_goTypes = []interface{}{
 | 
				
			||||||
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x8f, 0x3d, 0xce, 0x82, 0x40,
 | 
						(*Stack)(nil), // 0: stack.Stack
 | 
				
			||||||
	0x10, 0x86, 0xb3, 0xdf, 0xb2, 0x7c, 0x3a, 0x58, 0x98, 0xa9, 0x36, 0x56, 0x1b, 0x62, 0x41, 0x45,
 | 
						(*Frame)(nil), // 1: stack.Frame
 | 
				
			||||||
	0xa1, 0x47, 0x30, 0xa1, 0x32, 0x16, 0x78, 0x02, 0x84, 0x35, 0xd9, 0xc8, 0x5f, 0x76, 0x09, 0xd7,
 | 
					}
 | 
				
			||||||
	0xf0, 0xca, 0x66, 0x06, 0xb4, 0x7b, 0xde, 0x9f, 0xe4, 0x9d, 0x81, 0x24, 0x4c, 0x55, 0xfd, 0xca,
 | 
					var file_stack_proto_depIdxs = []int32{
 | 
				
			||||||
	0x47, 0x3f, 0x4c, 0x03, 0x2a, 0x16, 0xe9, 0x5b, 0x80, 0xba, 0x13, 0xe1, 0x11, 0xe2, 0xa7, 0xaf,
 | 
						1, // 0: stack.Stack.frames:type_name -> stack.Frame
 | 
				
			||||||
	0x3a, 0x1b, 0xb4, 0x30, 0x32, 0x4b, 0x4e, 0xbb, 0x7c, 0xa9, 0x17, 0x64, 0x96, 0x6b, 0x86, 0x1a,
 | 
						1, // [1:1] is the sub-list for method output_type
 | 
				
			||||||
	0xfe, 0xeb, 0xae, 0x69, 0x5d, 0x6f, 0xf5, 0x9f, 0x91, 0xd9, 0xb6, 0xfc, 0x4a, 0xdc, 0x83, 0x1c,
 | 
						1, // [1:1] is the sub-list for method input_type
 | 
				
			||||||
	0x5d, 0xa3, 0xa5, 0x11, 0x99, 0x2a, 0x09, 0xa9, 0x3b, 0x5b, 0x1f, 0xdc, 0xd0, 0xeb, 0xc8, 0x08,
 | 
						1, // [1:1] is the sub-list for extension type_name
 | 
				
			||||||
	0xea, 0xae, 0x12, 0x0f, 0xb0, 0xf1, 0x76, 0x76, 0x1c, 0x29, 0x8e, 0x7e, 0x3a, 0xbd, 0x80, 0xe2,
 | 
						1, // [1:1] is the sub-list for extension extendee
 | 
				
			||||||
	0x49, 0x44, 0x88, 0x6e, 0x55, 0x67, 0xb5, 0xe0, 0x02, 0x33, 0x79, 0x85, 0x6b, 0x69, 0x9b, 0x3d,
 | 
						0, // [0:1] is the sub-list for field type_name
 | 
				
			||||||
	0x62, 0xf2, 0xae, 0x74, 0xcf, 0xb2, 0xcc, 0xfc, 0x88, 0xf9, 0xc9, 0xf3, 0x27, 0x00, 0x00, 0xff,
 | 
					}
 | 
				
			||||||
	0xff, 0xfd, 0x2c, 0xbb, 0xfb, 0xf3, 0x00, 0x00, 0x00,
 | 
					
 | 
				
			||||||
 | 
					func init() { file_stack_proto_init() }
 | 
				
			||||||
 | 
					func file_stack_proto_init() {
 | 
				
			||||||
 | 
						if File_stack_proto != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							file_stack_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*Stack); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							file_stack_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*Frame); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						type x struct{}
 | 
				
			||||||
 | 
						out := protoimpl.TypeBuilder{
 | 
				
			||||||
 | 
							File: protoimpl.DescBuilder{
 | 
				
			||||||
 | 
								GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 | 
				
			||||||
 | 
								RawDescriptor: file_stack_proto_rawDesc,
 | 
				
			||||||
 | 
								NumEnums:      0,
 | 
				
			||||||
 | 
								NumMessages:   2,
 | 
				
			||||||
 | 
								NumExtensions: 0,
 | 
				
			||||||
 | 
								NumServices:   0,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							GoTypes:           file_stack_proto_goTypes,
 | 
				
			||||||
 | 
							DependencyIndexes: file_stack_proto_depIdxs,
 | 
				
			||||||
 | 
							MessageInfos:      file_stack_proto_msgTypes,
 | 
				
			||||||
 | 
						}.Build()
 | 
				
			||||||
 | 
						File_stack_proto = out.File
 | 
				
			||||||
 | 
						file_stack_proto_rawDesc = nil
 | 
				
			||||||
 | 
						file_stack_proto_goTypes = nil
 | 
				
			||||||
 | 
						file_stack_proto_depIdxs = nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						iofs "io/fs"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/continuity/fs"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Atime(st iofs.FileInfo) (time.Time, error) {
 | 
				
			||||||
 | 
						stSys, ok := st.Sys().(*syscall.Stat_t)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return time.Time{}, errors.Errorf("expected st.Sys() to be *syscall.Stat_t, got %T", st.Sys())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fs.StatATimeAsTime(stSys), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						iofs "io/fs"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Atime(st iofs.FileInfo) (time.Time, error) {
 | 
				
			||||||
 | 
						stSys, ok := st.Sys().(*syscall.Win32FileAttributeData)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return time.Time{}, fmt.Errorf("expected st.Sys() to be *syscall.Win32FileAttributeData, got %T", st.Sys())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// ref: https://github.com/golang/go/blob/go1.19.2/src/os/types_windows.go#L230
 | 
				
			||||||
 | 
						return time.Unix(0, stSys.LastAccessTime.Nanoseconds()), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,10 @@
 | 
				
			||||||
package system
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path.
 | 
					// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path.
 | 
				
			||||||
| 
						 | 
					@ -22,13 +23,13 @@ import (
 | 
				
			||||||
// d:\			--> Fail
 | 
					// d:\			--> Fail
 | 
				
			||||||
func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) {
 | 
					func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) {
 | 
				
			||||||
	if len(path) == 2 && string(path[1]) == ":" {
 | 
						if len(path) == 2 && string(path[1]) == ":" {
 | 
				
			||||||
		return "", fmt.Errorf("No relative path specified in %q", path)
 | 
							return "", errors.Errorf("No relative path specified in %q", path)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !filepath.IsAbs(path) || len(path) < 2 {
 | 
						if !filepath.IsAbs(path) || len(path) < 2 {
 | 
				
			||||||
		return filepath.FromSlash(path), nil
 | 
							return filepath.FromSlash(path), nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") {
 | 
						if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") {
 | 
				
			||||||
		return "", fmt.Errorf("The specified path is not on the system drive (C:)")
 | 
							return "", errors.New("The specified path is not on the system drive (C:)")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return filepath.FromSlash(path[2:]), nil
 | 
						return filepath.FromSlash(path[2:]), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,17 +16,14 @@ package otlptracegrpc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
 | 
						"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"google.golang.org/grpc"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
 | 
						coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
 | 
				
			||||||
	tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
 | 
						tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
 | 
				
			||||||
 | 
						"google.golang.org/grpc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type client struct {
 | 
					type client struct {
 | 
				
			||||||
| 
						 | 
					@ -38,10 +35,6 @@ type client struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ otlptrace.Client = (*client)(nil)
 | 
					var _ otlptrace.Client = (*client)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	errNoClient = errors.New("no client")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewClient creates a new gRPC trace client.
 | 
					// NewClient creates a new gRPC trace client.
 | 
				
			||||||
func NewClient(cc *grpc.ClientConn) otlptrace.Client {
 | 
					func NewClient(cc *grpc.ClientConn) otlptrace.Client {
 | 
				
			||||||
	c := &client{}
 | 
						c := &client{}
 | 
				
			||||||
| 
						 | 
					@ -73,7 +66,7 @@ func (c *client) Stop(ctx context.Context) error {
 | 
				
			||||||
// UploadTraces sends a batch of spans to the collector.
 | 
					// UploadTraces sends a batch of spans to the collector.
 | 
				
			||||||
func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error {
 | 
					func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error {
 | 
				
			||||||
	if !c.connection.Connected() {
 | 
						if !c.connection.Connected() {
 | 
				
			||||||
		return fmt.Errorf("traces exporter is disconnected from the server: %w", c.connection.LastConnectError())
 | 
							return errors.Wrap(c.connection.LastConnectError(), "traces exporter is disconnected from the server")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx, cancel := c.connection.ContextWithStop(ctx)
 | 
						ctx, cancel := c.connection.ContextWithStop(ctx)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										7
									
								
								vendor/github.com/moby/buildkit/util/tracing/otlptracegrpc/errors.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										7
									
								
								vendor/github.com/moby/buildkit/util/tracing/otlptracegrpc/errors.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					package otlptracegrpc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						errNoClient = errors.New("no client")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
// +build !windows
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package term
 | 
					package term
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
// +build !windows
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Package term provides structures and helper functions to work with
 | 
					// Package term provides structures and helper functions to work with
 | 
				
			||||||
| 
						 | 
					@ -14,10 +15,8 @@ import (
 | 
				
			||||||
	"golang.org/x/sys/unix"
 | 
						"golang.org/x/sys/unix"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					// ErrInvalidState is returned if the state of the terminal is invalid.
 | 
				
			||||||
	// ErrInvalidState is returned if the state of the terminal is invalid.
 | 
					var ErrInvalidState = errors.New("Invalid terminal state")
 | 
				
			||||||
	ErrInvalidState = errors.New("Invalid terminal state")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// State represents the state of the terminal.
 | 
					// State represents the state of the terminal.
 | 
				
			||||||
type State struct {
 | 
					type State struct {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,10 +66,6 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and
 | 
					 | 
				
			||||||
	// STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as
 | 
					 | 
				
			||||||
	// go-ansiterm hasn't switch to x/sys/windows.
 | 
					 | 
				
			||||||
	// TODO: switch back to x/sys/windows once go-ansiterm has switched
 | 
					 | 
				
			||||||
	if emulateStdin {
 | 
						if emulateStdin {
 | 
				
			||||||
		h := uint32(windows.STD_INPUT_HANDLE)
 | 
							h := uint32(windows.STD_INPUT_HANDLE)
 | 
				
			||||||
		stdIn = windowsconsole.NewAnsiReader(int(h))
 | 
							stdIn = windowsconsole.NewAnsiReader(int(h))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					//go:build !windows
 | 
				
			||||||
// +build !windows
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package term
 | 
					package term
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					//go:build darwin || freebsd || openbsd || netbsd
 | 
				
			||||||
// +build darwin freebsd openbsd netbsd
 | 
					// +build darwin freebsd openbsd netbsd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package term
 | 
					package term
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
//+build !darwin,!freebsd,!netbsd,!openbsd,!windows
 | 
					//go:build !darwin && !freebsd && !netbsd && !openbsd && !windows
 | 
				
			||||||
 | 
					// +build !darwin,!freebsd,!netbsd,!openbsd,!windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package term
 | 
					package term
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					//go:build windows
 | 
				
			||||||
// +build windows
 | 
					// +build windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package windowsconsole
 | 
					package windowsconsole
 | 
				
			||||||
| 
						 | 
					@ -190,7 +191,6 @@ func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) stri
 | 
				
			||||||
		// <Ctrl>-S  Suspends printing on the screen (does not stop the program).
 | 
							// <Ctrl>-S  Suspends printing on the screen (does not stop the program).
 | 
				
			||||||
		// <Ctrl>-U  Deletes all characters on the current line. Also called the KILL key.
 | 
							// <Ctrl>-U  Deletes all characters on the current line. Also called the KILL key.
 | 
				
			||||||
		// <Ctrl>-E  Quits current command and creates a core
 | 
							// <Ctrl>-E  Quits current command and creates a core
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// <Alt>+Key generates ESC N Key
 | 
						// <Alt>+Key generates ESC N Key
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					//go:build windows
 | 
				
			||||||
// +build windows
 | 
					// +build windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package windowsconsole
 | 
					package windowsconsole
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue