Compare commits

...

241 Commits
v1.1.0 ... main

Author SHA1 Message Date
Stefan Berger d9809a71af
Merge pull request #183 from containerd/dependabot/go_modules/cmd/github.com/containerd/containerd/v2-2.0.5
build(deps): bump github.com/containerd/containerd/v2 from 2.0.4 to 2.0.5 in /cmd
2025-05-21 14:37:12 -04:00
dependabot[bot] 05f86fe582
build(deps): bump github.com/containerd/containerd/v2 in /cmd
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.4...v2.0.5)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.0.5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 18:04:19 +00:00
Stefan Berger 775f5bc5b1
Merge pull request #182 from containerd/dependabot/go_modules/golang.org/x/net-0.38.0
build(deps): bump golang.org/x/net from 0.36.0 to 0.38.0
2025-05-06 21:31:24 -04:00
dependabot[bot] 4b96783b80
build(deps): bump golang.org/x/net from 0.36.0 to 0.38.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 01:25:23 +00:00
Stefan Berger e353f0f2e2
Merge pull request #180 from containerd/dependabot/go_modules/cmd/golang.org/x/net-0.38.0
build(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 in /cmd
2025-05-06 21:23:51 -04:00
dependabot[bot] ef5ba82fd6
build(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 in /cmd
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 23:19:10 +00:00
Stefan Berger c377ec98ff
Merge pull request #178 from stefanberger/stefanberger/x-net-to-0.36.0
build(deps): bump golang.org/x/net to v0.36.0
2025-03-17 21:41:40 -04:00
Stefan Berger fd725bccd6 build(deps): Run go mod tidy in cmd/
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2025-03-17 21:36:35 -04:00
Stefan Berger 7d9a2f7377 build(deps): bump golang.org/x/net to v0.36.0
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2025-03-17 21:30:19 -04:00
Stefan Berger a34eeebb29 build(deps): bump github.com/containerd/containerd/v2 to v2.0.4
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2025-03-17 21:28:41 -04:00
Stefan Berger 9dc598742a
Merge pull request #177 from stefanberger/stefanberger/x-net-to-0.36.0
ci: Ditch go 1.22 and use 1.24
2025-03-17 21:23:19 -04:00
Stefan Berger 9ea167fbc8 ci: Ditch go 1.22 and use 1.24
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2025-03-17 20:54:05 -04:00
Stefan Berger 1421946f68
Merge pull request #175 from containerd/dependabot/go_modules/cmd/github.com/containerd/containerd/v2-2.0.4
build(deps): bump github.com/containerd/containerd/v2 from 2.0.0 to 2.0.4 in /cmd
2025-03-17 20:42:44 -04:00
dependabot[bot] d7dbabf80a
build(deps): bump github.com/containerd/containerd/v2 in /cmd
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.0 to 2.0.4.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.0...v2.0.4)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-18 00:37:31 +00:00
Stefan Berger c88b16a6b7
Merge pull request #176 from stefanberger/stefanberger/containerd_2_0_4
ci: Upgrade to use containerd/project-checks@v1.2.2
2025-03-17 20:36:27 -04:00
Stefan Berger 96eb3e3b18 ci: Upgrade to use containerd/project-checks@v1.2.2
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2025-03-17 20:28:44 -04:00
Stefan Berger f304b180ae
Merge pull request #173 from containerd/dependabot/go_modules/cmd/github.com/go-jose/go-jose/v4-4.0.5
build(deps): bump github.com/go-jose/go-jose/v4 from 4.0.4 to 4.0.5 in /cmd
2025-02-25 12:57:40 -05:00
dependabot[bot] 0788082004
build(deps): bump github.com/go-jose/go-jose/v4 in /cmd
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.0.4 to 4.0.5.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.0.4...v4.0.5)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-24 22:54:11 +00:00
Stefan Berger be59467a24
Merge pull request #172 from stefanberger/update_dependencies
build(deps): Update dependencies due to high severity issues
2024-12-18 22:04:21 -05:00
Stefan Berger 744c7cab90 build(deps): Update dependencies due to high severity issues
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
2024-12-18 21:56:00 -05:00
Akihiro Suda 1e301ef262
Merge pull request #170 from stefanberger/update_dependencies
Update dependencies
2024-12-17 02:22:39 +09:00
Stefan Berger f66a7fd67e build(deps): Update a few dependencies
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-12-16 11:21:54 -05:00
Stefan Berger e7d76cbf12 github: Update to golangci-lint v1.62.2
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-12-11 11:55:11 -05:00
Stefan Berger febb6781c3 github: Use golang v1.23 ditching v1.21
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-12-11 11:55:11 -05:00
Stefan Berger 01abc52f31
Merge pull request #169 from AkihiroSuda/dev
go.mod: github.com/containerd/containerd/v2 v2.0.0
2024-12-10 08:31:36 -05:00
Akihiro Suda 5c12942714
go.mod: github.com/containerd/containerd/v2 v2.0.0
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-12-10 17:49:17 +09:00
Stefan Berger ed83e83c10
Merge pull request #168 from dmcgowan/imgcrypt-v2
Update go module to imgcrypt v2
2024-10-24 10:15:12 -04:00
Derek McGowan 8f67ad5859 Update go module to imgcrypt v2
Allow importing of containerd v2 without breaking compatibility for
use with containerd v1.

Signed-off-by: Derek McGowan <derek@mcg.dev>
2024-10-24 07:06:14 -07:00
Stefan Berger 1711ee6e48
Merge pull request #165 from AkihiroSuda/dev
go.mod: github.com/containerd/containerd/v2 v2.0.0-rc.6
2024-10-24 08:43:05 -04:00
Stefan Berger 2683bd0f9d
Merge pull request #166 from containerd/stefanberger/upgrade_golangci
Upgrade golangci-lint to v1.61.0
2024-10-24 08:14:14 -04:00
Stefan Berger 0fb4a7569f Upgrade golangci-lint to v1.61.0
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-10-24 08:05:04 -04:00
Akihiro Suda b71cf225e1
go.mod: github.com/containerd/containerd/v2 v2.0.0-rc.6
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-10-24 19:11:01 +09:00
Stefan Berger c091455481
Merge pull request #164 from containerd/dependabot/go_modules/google.golang.org/grpc-1.64.1
build(deps): bump google.golang.org/grpc from 1.64.0 to 1.64.1
2024-07-23 21:36:06 -04:00
dependabot[bot] bdc9fd0b1e
build(deps): bump google.golang.org/grpc from 1.64.0 to 1.64.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.64.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.64.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-24 01:28:29 +00:00
Stefan Berger f3769dc3e4
Merge pull request #163 from containerd/dependabot/go_modules/cmd/google.golang.org/grpc-1.64.1
build(deps): bump google.golang.org/grpc from 1.64.0 to 1.64.1 in /cmd
2024-07-09 18:30:13 -04:00
dependabot[bot] b1ff2f6434
build(deps): bump google.golang.org/grpc from 1.64.0 to 1.64.1 in /cmd
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.64.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.64.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-09 21:58:18 +00:00
Stefan Berger 225e38095f
Merge pull request #162 from stefanberger/stefanberger/changelog-v1.2.0-rc1
CHANGES: Updated CHANGES document for 1.2.0-rc1 release
2024-07-01 13:10:37 -04:00
Stefan Berger 38accb1ac5 CHANGES: Updated CHANGES document for 1.2.0-rc1 release
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-07-01 10:16:51 -04:00
Stefan Berger 06b4a7160a
Merge pull request #161 from stefanberger/stefanberger/upgrade_ocicrypt
build(deps): update to ocicrypt v1.2.0
2024-07-01 10:14:46 -04:00
Stefan Berger 8c4135efc9 build(deps): update to ocicrypt v1.2.0
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-07-01 10:02:31 -04:00
Stefan Berger 8ae787b572
Merge pull request #160 from containerd/stefanberger/upgrade_golangci
Upgrade to latest golangci-lint and fix an issue
2024-07-01 07:29:21 -04:00
Stefan Berger 882246a6fd Upgrade golangci-lint to v1.59.1
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-06-30 20:08:58 -04:00
Stefan Berger dad9c0b442 Rename unused function parameter to _
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-06-30 20:07:11 -04:00
Akihiro Suda 6b4ba3f062
Merge pull request #158 from thaJeztah/bump_containerd_rc3
go.mod: github.com/containerd/containerd v2.0.0-rc.3
2024-06-22 12:18:51 +09:00
Sebastiaan van Stijn bc04b3cc65
go.mod: github.com/containerd/containerd v2.0.0-rc.3
rc.3 rmoves pkg/seed, so removing that import from cmd/ctr

full diff: https://github.com/containerd/containerd/compare/v2.0.0-rc.2...v2.0.0-rc.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-22 01:06:51 +02:00
Derek McGowan 3ca09a2db5
Merge pull request #157 from dmcgowan/update-containerd-v2.0.0-rc.2
Update containerd to v2.0.0-rc.2
2024-05-28 13:38:04 -07:00
Derek McGowan 0abc1115d3 Update test to pull all bash platforms
Signed-off-by: Derek McGowan <derek@mcg.dev>
2024-05-26 21:40:13 -07:00
Derek McGowan 8b9b205467 Update to containerd v2.0.0-rc.2
Signed-off-by: Derek McGowan <derek@mcg.dev>
2024-05-26 17:27:03 -07:00
Akihiro Suda 27550399fb
go.mod: github.com/containerd/containerd/v2 v2.0.0-rc.1
- github.com/containerd/containerd/{containers,images,...} -> github.com/containerd/containerd/v2/core/{containers,images,...}
- github.com/containerd/containerd/{errdefs,logs,platforms} -> github.com/containerd/{errdefs,logs,platforms}
- github.com/containerd/typeurl -> github.com/containerd/typeurl/v2
- github.com/urfave/cli -> github.com/urfave/cli/v2 (See containerd/containerd PR 9809)
- github.com/gogo/protobuf -> github.com/containerd/containerd/v2/protobuf

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-04-25 12:01:20 +09:00
Akihiro Suda 85438ace2e
GetImageLayerDescriptors: handle application/vnd.in-toto+json
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-04-25 11:59:58 +09:00
Akihiro Suda ae8d059087
script/tests/test_encryption.sh: debug
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-04-25 11:59:58 +09:00
Akihiro Suda 964f4c40ab
script/tests/test_encryption.sh: show log
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-04-25 11:59:55 +09:00
Stefan Berger 619fd33080
Merge pull request #154 from containerd/dependabot/go_modules/golang.org/x/net-0.23.0
build(deps): bump golang.org/x/net from 0.17.0 to 0.23.0
2024-04-19 16:03:22 -04:00
dependabot[bot] 1839e20d51
build(deps): bump golang.org/x/net from 0.17.0 to 0.23.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.17.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.17.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-19 19:58:40 +00:00
Stefan Berger c9a63f4e18
Merge pull request #155 from containerd/dependabot/go_modules/cmd/golang.org/x/net-0.23.0
build(deps): bump golang.org/x/net from 0.17.0 to 0.23.0 in /cmd
2024-04-19 15:57:03 -04:00
dependabot[bot] 33dc459b2c
build(deps): bump golang.org/x/net from 0.17.0 to 0.23.0 in /cmd
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.17.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.17.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-19 12:39:27 +00:00
Akihiro Suda f311589503
Merge pull request #152 from AkihiroSuda/carry-50
[Carry #50] separate command-line to a separate module
2024-04-04 12:11:07 +09:00
Sebastiaan van Stijn cb7e5d33ff
separate command-line to a separate module
(This is a very quick attempt at "what this will look like")

This repository provides both command-line utilities, and a module for external
consumers.

Currently, both are part of the same module; as a result, dependencies of both
the module *and* the command-line utilities are listed in the repositories go.mod.
This affects consumers of this project, because (due to go module's nature of
dependency (version) resolution), those consumers will inherit all dependencies,
or will be "forced" to use the same version of the CLI dependencies.

This is a very quick attempt at separating the CLI utilities from the "module",
by creating a separate go.mod (and module) for the CLI utilities.

I'm not fond of the name (github.com/containerd/imgcrypt/cmd) for that module
(possibly renaming to github.com/containerd/imgcrypt/cli would be slightly
clearer).

This change _will_ add some additional work when tagging releases; a separate tag
should be created for the cli utilities (tagging as `cmd/vX.Y.Z`), and the
"github.com/containerd/imgcrypt" dependency in the go.mod inside the cmd directory
may need to be updated to reflect the latest version of the main module when tagging
new releases (as the replace rule is non-transitional); something like:

1. update `github.com/containerd/imgcrypt` version in cmd/go.mod to "next release"
2. tag both `v<new release>` and `cmd/v<new version>` in tandem.

CI / validation also needs to be updated to verify both `go.mod` and `go.sum`
files are correct / up-to-date. Possibly checks should be added to make sure the
main module is isolated from the cmd module (i.e., the "module" should not import
any path from the cmd directory: the reverse is of course OK (and expected)).

Finally; use of the 'vendor' directory may need to be discussed; it is common
to only use a vendor directory for projects that produce binaries, but omit the
vendor directory for "library" projects. In this case (if vendoring is still
desired), the vendor directory should be removed from the root of the repository,
and moved inside the `cmd` directory.

Originally-from: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-03-28 14:17:54 +09:00
Mike Brown 261561143c
Merge pull request #151 from AkihiroSuda/typo
typo: disable_plugins -> disabled_plugins
2024-03-27 09:51:14 -05:00
Mike Brown 4bc12ca1c8
Merge pull request #150 from AkihiroSuda/remove-unused-variables
Remove unused variables
2024-03-27 09:50:20 -05:00
Akihiro Suda 04e37ceefe
typo: disable_plugins -> disabled_plugins
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-03-27 23:35:42 +09:00
Akihiro Suda 9eee3300d1
Remove unused variables
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-03-27 23:34:41 +09:00
Stefan Berger 9eba5fc3f6
Merge pull request #146 from containerd/dependabot/go_modules/google.golang.org/protobuf-1.33.0
build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.33.0
2024-03-13 21:13:59 -04:00
dependabot[bot] eb84bc1822
build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.33.0
Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 23:17:54 +00:00
Stefan Berger e093236d13
Merge pull request #145 from stefanberger/changes
CHANGES: Updated CHANGES document for 1.1.10 release
2024-03-13 11:13:01 -04:00
Stefan Berger 79e18551fe CHANGES: Updated CHANGES document for 1.1.10 release
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-03-13 11:04:42 -04:00
Stefan Berger f2b9a8971a
Merge pull request #144 from stefanberger/add_jwk_rsa_keys
test: Add 2 more RSA keys with 'alg' explicitly set
2024-03-13 10:56:55 -04:00
Stefan Berger f37a4d8871 test: Add 2 more RSA keys with 'alg' explicitly set
Besides the existing key with no 'alg' set, add 2 more RSA keys have
'alg' set to RSA-OAEP and RSA-OAEP-256. All of these are synonymous for
each other, meaning they use SHA256 and MGF1.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-03-13 10:49:39 -04:00
Stefan Berger c99d9f8c01
Merge pull request #143 from containerd/add_jwk_ec_test
test: Add a test with JWK EC key(P-521, ECDH-ES+A128KW)
2024-03-13 10:34:14 -04:00
Stefan Berger e31dd549a4 test: Add a test with JWK EC key(P-521, ECDH-ES+A128KW)
Duplicate existing test with RSA key and use it for testing with EC key.

Key created on website: https://mkjwk.org/

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-03-13 10:14:52 -04:00
Stefan Berger a6814bff0c
Merge pull request #142 from stefanberger/stefanberger/ocicrypt1_1_10
Update to ocicrypt v1.1.10 and sync ctr with containerd 1.6.30
2024-03-11 21:52:21 -04:00
Stefan Berger 0778f7ec89 ctr: Fix issues in the help screens for image en- and decryption
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-03-11 21:45:21 -04:00
Stefan Berger b32797501b ctr: Sync code with containerd v1.6.30 ctr
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-03-11 21:37:48 -04:00
Stefan Berger 24ca7757bc build(deps): Update to ocicrypt v1.1.10
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-03-11 21:12:13 -04:00
Stefan Berger 47481fe343
Merge pull request #140 from containerd/dependabot/go_modules/github.com/go-jose/go-jose/v3-3.0.3
build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3
2024-03-07 18:43:54 -05:00
dependabot[bot] 67dc8ef639
build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3
Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.1 to 3.0.3.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/v3.0.3/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v3.0.1...v3.0.3)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 23:32:22 +00:00
Stefan Berger 10e6b83efa
Merge pull request #141 from stefanberger/abandon_go_1_20
ci: Abandon go 1.20 in favor of 1.22
2024-03-07 18:30:04 -05:00
Stefan Berger 677bdf6aa2 ci: Abandon go 1.20 in favor of 1.22
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-03-07 18:28:56 -05:00
Stefan Berger 3399700388
Merge pull request #137 from containerd/dependabot/go_modules/github.com/opencontainers/runc-1.1.12
build(deps): bump github.com/opencontainers/runc from 1.1.5 to 1.1.12
2024-01-31 17:58:32 -05:00
dependabot[bot] 66b389c0a7
build(deps): bump github.com/opencontainers/runc from 1.1.5 to 1.1.12
Bumps [github.com/opencontainers/runc](https://github.com/opencontainers/runc) from 1.1.5 to 1.1.12.
- [Release notes](https://github.com/opencontainers/runc/releases)
- [Changelog](https://github.com/opencontainers/runc/blob/v1.1.12/CHANGELOG.md)
- [Commits](https://github.com/opencontainers/runc/compare/v1.1.5...v1.1.12)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/runc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-31 22:53:37 +00:00
Stefan Berger 85b4bab9c6
Merge pull request #134 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.6.26
build(deps): bump github.com/containerd/containerd from 1.6.23 to 1.6.26
2023-12-28 15:46:49 -05:00
dependabot[bot] 12ec12a288
build(deps): bump github.com/containerd/containerd from 1.6.23 to 1.6.26
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.23 to 1.6.26.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.23...v1.6.26)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-28 20:38:35 +00:00
Stefan Berger 2b7dd2fa8f
Merge pull request #136 from containerd/stefanberger/staticcheck
ctr: Avoid staticcheck on imported file
2023-12-28 15:36:50 -05:00
Stefan Berger 264dfc7467 ctr: Avoid staticcheck on imported file
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-12-28 15:30:41 -05:00
Stefan Berger 1292dc0759
Merge pull request #135 from containerd/stefanberger/staticcheck
ctr: Avoid staticcheck on seed.WithTimeAndRand
2023-12-28 15:22:27 -05:00
Stefan Berger 3c4442c99a ctr: Avoid staticcheck on seed.WithTimeAndRand
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-12-28 15:15:46 -05:00
Stefan Berger 1f855f4065
Merge pull request #133 from containerd/dependabot/go_modules/golang.org/x/crypto-0.17.0
build(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0
2023-12-19 21:18:43 -05:00
dependabot[bot] 5d4d0bd662
build(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 23:46:13 +00:00
Stefan Berger fcc5cb436d
Merge pull request #132 from containerd/dependabot/go_modules/github.com/go-jose/go-jose/v3-3.0.1
build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.0 to 3.0.1
2023-11-21 17:50:34 -05:00
dependabot[bot] be77c6397a
build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.0 to 3.0.1
Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/v3/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-21 22:22:40 +00:00
Stefan Berger 90513b0797
Merge pull request #131 from stefanberger/stefanberger/changes
CHANGES: Updated CHANGES document for 1.1.9 release
2023-10-31 10:27:31 -04:00
Stefan Berger 433168532d CHANGES: Updated CHANGES document for 1.1.9 release
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-10-31 10:16:16 -04:00
Stefan Berger a4fca4ff48
Merge pull request #130 from stefanberger/stefanberger/ocicrypt_v1_1_9
build(deps): Update to ocicrypt v1.1.9
2023-10-31 10:10:16 -04:00
Stefan Berger 3e60defd72 build(deps): Update to ocicrypt v1.1.9
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-10-31 10:00:39 -04:00
Stefan Berger 42c23514a5
Merge pull request #124 from containerd/dependabot/go_modules/golang.org/x/net-0.17.0
build(deps): bump golang.org/x/net from 0.8.0 to 0.17.0
2023-10-25 21:19:04 -04:00
dependabot[bot] ed2700796f
build(deps): bump golang.org/x/net from 0.8.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.8.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.8.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 01:06:15 +00:00
Stefan Berger bbc25e0de2
Merge pull request #126 from containerd/dependabot/go_modules/google.golang.org/grpc-1.56.3
build(deps): bump google.golang.org/grpc from 1.53.0 to 1.56.3
2023-10-25 21:05:24 -04:00
dependabot[bot] 1d935804d2
build(deps): bump google.golang.org/grpc from 1.53.0 to 1.56.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.53.0 to 1.56.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.53.0...v1.56.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 00:58:07 +00:00
Stefan Berger 7599f6201a
Merge pull request #129 from stefanberger/stefanberger/build_runc_v1.1.9
CI/CD: Build runc v1.1.9
2023-10-25 20:56:56 -04:00
Stefan Berger 330a079fcc CI/CD: Build runc v1.1.9
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-10-25 20:47:49 -04:00
Stefan Berger 061ead6edb
Merge pull request #122 from stefanberger/stefanberger/imgcrypt_v1_1_8
CHANGES: Updated CHANGES document for 1.1.8 release
2023-08-15 12:33:25 -04:00
Stefan Berger 956b4d3fe3 CHANGES: Updated CHANGES document for 1.1.8 release
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 12:23:56 -04:00
Stefan Berger 3fad0e10f7
Merge pull request #120 from stefanberger/stefanberger/sync_ctr_1_6_23
Synchronize enc-ctr with upstream ctr from containerd v1.6.23 and use containerd v1.6.23 in dependency
2023-08-15 10:36:05 -04:00
Stefan Berger 9e8e1c1df3 ctr: Sync code with containerd v1.6.23 ctr
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 10:28:46 -04:00
Stefan Berger 7d2cca5efd build(deps): bump containerd from 1.6.20 to 1.6.23
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 08:47:39 -04:00
Stefan Berger f61376667f
Merge pull request #119 from stefanberger/stefanberger/sync_ctr_1_6_20
Synchronize enc-ctr with upstream ctr from containerd v1.6.20
2023-08-15 08:42:46 -04:00
Stefan Berger 0f2559e3c9 ctr: Sync code with containerd v1.6.20 ctr
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 08:36:11 -04:00
Stefan Berger c48dd78700 cmd: Copy IntToInt32Array into img package and use it
Since IntToInt32Array was removed from commands package, move it into
out own img package.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 08:16:30 -04:00
Stefan Berger 80abfcd916
Merge pull request #118 from stefanberger/stefanberger/update_ocicrypt_1_1_8
Update to ocicrypt 1.1.8 and minimum go 1.20
2023-08-15 08:12:36 -04:00
Stefan Berger 6d48a4ecc3 build(deps): bump ocicrypt from 1.1.7 to 1.1.8
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 08:01:35 -04:00
Stefan Berger 1bc94a206e github: Use golangci-lint v1.54.1 and adjust config file
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 08:01:35 -04:00
Stefan Berger 9065f1da9e github: Test with go 1.21 and go 1.20
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 08:01:35 -04:00
Stefan Berger 74986f3687 go.mod: Require go 1.20
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-08-15 08:01:35 -04:00
Stefan Berger 7a5fb7db9b
Merge pull request #117 from containerd/dependabot/go_modules/google.golang.org/grpc-1.53.0
build(deps): bump google.golang.org/grpc from 1.47.0 to 1.53.0
2023-07-05 20:00:42 -04:00
dependabot[bot] a2a8273187
build(deps): bump google.golang.org/grpc from 1.47.0 to 1.53.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.47.0 to 1.53.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.47.0...v1.53.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-05 21:24:13 +00:00
Stefan Berger be90a95e8d
Merge pull request #116 from stefanberger/stefanberger/add_jwk_key_file_test
test: Test creating and running of container with key file missing
2023-06-16 06:59:59 -04:00
Stefan Berger 286470a956 test: Test creating and running of container with key file missing
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-06-15 21:23:54 -04:00
Stefan Berger 0512869879
Merge pull request #115 from stefanberger/stefanberger/test_fixes
Fix some issues in the test script
2023-06-15 19:21:27 -04:00
Stefan Berger aa517cc776 test: Fix order of parameters and remove unnecessary key parameter
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-06-15 19:07:13 -04:00
Stefan Berger ec7231185e test: Add comments to test case
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-06-15 15:46:49 -04:00
Stefan Berger 2959ec0ec4 test: To be able to run testLocalKeys alone add missing env variable
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-06-15 15:46:49 -04:00
Stefan Berger e7500301ca
Merge pull request #112 from stefanberger/stefanberger/containerd_1_6_20
build(deps): upgrade github.com/containerd/containerd from 1.6.18 to …
2023-05-01 11:54:12 -04:00
Stefan Berger a7f2760c71 build(deps): upgrade github.com/containerd/containerd from 1.6.18 to 1.6.20
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-05-01 11:40:54 -04:00
Stefan Berger 03e241bcf2
Merge pull request #113 from stefanberger/stefanberger/golangci_list_1_52_2
ci: Update golangci-lint to v1.52.2
2023-05-01 11:35:09 -04:00
Stefan Berger 002abac5a5 images: Change 'any' to 'anything' to avoid clash with built-in type 'any'
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-05-01 11:30:48 -04:00
Stefan Berger 5780ecc88b images: Replace unused function parameters with '_'
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-05-01 11:18:29 -04:00
Stefan Berger 7dc85928e2 ci: Update golangci-lint to v1.52.2
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-05-01 10:28:04 -04:00
Stefan Berger cfdad3414f
Merge pull request #109 from containerd/dependabot/go_modules/github.com/opencontainers/runc-1.1.5
build(deps): bump github.com/opencontainers/runc from 1.1.2 to 1.1.5
2023-03-30 06:56:18 -04:00
dependabot[bot] 90e4f77bdc
build(deps): bump github.com/opencontainers/runc from 1.1.2 to 1.1.5
Bumps [github.com/opencontainers/runc](https://github.com/opencontainers/runc) from 1.1.2 to 1.1.5.
- [Release notes](https://github.com/opencontainers/runc/releases)
- [Changelog](https://github.com/opencontainers/runc/blob/v1.1.5/CHANGELOG.md)
- [Commits](https://github.com/opencontainers/runc/compare/v1.1.2...v1.1.5)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/runc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-29 23:49:35 +00:00
Stefan Berger d50cadb5bf
Merge pull request #110 from stefanberger/stefanberger/abandon_go_1.18
Abandon go 1.18 (end-of-life) and use 1.19 and 1.20 in tests
2023-03-29 19:48:02 -04:00
Stefan Berger 8fc037fd2d tests: Upgrade toml written by test case to version 2
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-03-29 19:02:54 -04:00
Stefan Berger 0b31beb1c7 ci: Run tests with go 1.19 and 1.20 (abandon 1.18)
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-03-29 16:40:39 -04:00
Stefan Berger 523674c781 build(deps): Update to minimum required go v1.19
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-03-29 12:39:59 -04:00
Stefan Berger 92bd74222a
Merge pull request #107 from stefanberger/stefanberger/upgrade_golang_org_x_net_to_v0.7
Update to golang.org/x/net@v0.7.0 and github.com/containers/ocicrypt@v1.1.7
2023-02-21 19:40:11 -05:00
Stefan Berger 96a2314e83 build(deps): Upgrade to github.com/containers/ocicrypt@v1.1.7
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-02-19 15:17:30 -05:00
Stefan Berger 1c5055514a bulid(deps): Update to golang.org/x/net@v0.7.0
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-02-19 15:15:40 -05:00
Stefan Berger 9645d39f07 build(deps): Update to minimum required go v1.18
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-02-19 11:29:52 -05:00
Stefan Berger 9a7c29f65d
Merge pull request #106 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.6.18
build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.18
2023-02-16 09:35:01 -05:00
dependabot[bot] 8daaa45a63
build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.18
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.12 to 1.6.18.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.12...v1.6.18)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-16 14:19:40 +00:00
Stefan Berger 8a453e8b6a
Merge pull request #105 from stefanberger/stefanberger/fix_typo_in_readme
README: Fix a typo
2023-01-30 07:14:09 -05:00
Stefan Berger 12e84f51fb README: Fix a typo
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2023-01-30 06:59:36 -05:00
Stefan Berger 277688a852
Merge pull request #103 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.6.12
build(deps): bump github.com/containerd/containerd from 1.6.8 to 1.6.12
2022-12-08 07:03:08 -05:00
dependabot[bot] 4e5a73e393
build(deps): bump github.com/containerd/containerd from 1.6.8 to 1.6.12
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.8 to 1.6.12.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.8...v1.6.12)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-07 23:42:13 +00:00
Stefan Berger 8314a7111e
Merge pull request #101 from austinvazquez/update-golangci-lint
Update golangci-lint to v1.50.1
2022-11-10 08:20:28 -05:00
Austin Vazquez 16a071b983 Update golangci-lint to v1.50.1
* Updated golangci-lint version
* Removed deprecated linters

Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2022-11-10 01:36:19 +00:00
Stefan Berger d0367e9871
Merge pull request #100 from austinvazquez/remove-ioutil
Remove references to package io/ioutil
2022-11-09 20:32:46 -05:00
Stefan Berger b3ac483e93
Merge pull request #99 from austinvazquez/update-github-actions-ci-workflow
Update GitHub actions CI workflow
2022-11-09 20:25:55 -05:00
Austin Vazquez 06827a1d86 Update containerd project checks package in CI
* containerd/project-checks from v1 to v1.1.0

Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2022-11-10 00:50:50 +00:00
Austin Vazquez f6a39e1bcd Update GitHub actions packages in CI workflow
* actions/checkout and actions/setup-go from v2 to v3.

Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2022-11-10 00:50:50 +00:00
Austin Vazquez 6383351756 Update GitHub actions CI workflow OS runner images
* Ubuntu-18.04 to Ubuntu-22.04
* Windows-2019 to Windows-2022

Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2022-11-10 00:50:47 +00:00
Austin Vazquez 981a3fdd5a Remove references to package io/ioutil
Package io/ioutil has been marked deprecated in Go 1.16.

Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2022-11-10 00:07:32 +00:00
Stefan Berger 4449cebf48
Merge pull request #98 from stefanberger/stefanberger/codeql
CI/CD: Run CodeQL on PRs and once a month
2022-10-17 06:57:36 -04:00
Stefan Berger b6e16db881 CI/CD: Run CodeQL on PRs and once a month
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-10-16 10:42:44 -04:00
Stefan Berger 01a05dffff
Merge pull request #97 from stefanberger/stefanberger/for-v1.1.7
CHANGES: Updated CHANGES document for 1.1.7 release
2022-10-14 15:39:09 -04:00
Stefan Berger 17e5e7f790 CHANGES: Updated CHANGES document for 1.1.7 release
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-10-14 13:31:42 -04:00
Stefan Berger ea9270972e
Merge pull request #96 from stefanberger/stefanberger/zstd
Update to ocicrypt 1.1.6 and add support for zstd type of compressed layers
2022-10-10 20:34:52 -04:00
Stefan Berger 06da359b73 Add support for zstd type of compressed layers
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Resolves: https://github.com/containerd/imgcrypt/issues/95
2022-10-07 12:24:22 -04:00
Stefan Berger 4a51045e4c build(deps): Update to ocicrypt 1.1.6
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-10-07 10:43:20 -04:00
Stefan Berger 2c93cef1eb ctr: Document that import of encrypted image requires decryption key
The import of an encrypted image requires the decryption key as proof
that one posses one of the decryption keys of the image. However, the
image will not be decrypted as part of the import. The alternative path
that does not require the decryption key is to pull the image from a
repository.

The underlying reason why one needs the key is because containerd sends
an encrypted layer to ctd-decoder which needs the decryption key for the
decryption of that layer and for the import to succeed.

It is not currently clear what the layer represents and why it is part
of an exported image. The layer that is sent for the current alpine
image is the layer with the hash ff7f8bb.. from here:

./blobs/sha256/8a1591...:
{"architecture":"amd64",[...]
 {"type":"layers",
  "diff_ids":
    ["sha256:ff7f8bbf1c81b508f82b1c59e8c2467175c0b33e58a79507f4fde8067d6f1897",
     "sha256:c32387d564776805eb144718cd41629761e1980280c9d512df358b60f9fe6ba3"]
 }
}

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-09-19 09:22:12 -04:00
Stefan Berger 44f4e187e2 ctr: Add support for --all-platforms to encrypt command
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-09-19 09:22:12 -04:00
Stefan Berger d9fccdc463 ctr: Sync with upstream ctr and add --skip-digest-for-named opt to import
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-09-19 09:22:12 -04:00
Stefan Berger b8f807f68a ctr: Sync with upstream ctr and add --platform option to import
Signed-off-by: Stefan Berger <stefab@linux.ibm.com>
2022-09-19 09:22:12 -04:00
Stefan Berger 3c7c6b10d5
Merge pull request #92 from stefanberger/stefanberger/containerd_1_6_8
build(deps): Update to containerd 1.6.8
2022-09-19 07:11:30 -04:00
Stefan Berger 07dd48dc69 build(deps): Update to containerd 1.6.8
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-09-18 20:17:58 -04:00
Stefan Berger 810cd594d9
Merge pull request #90 from stefanberger/stefanberger/openssl3
tests: Add -traditional to OpenSSL command line when OSSL v3 is used
2022-09-18 16:58:51 -04:00
Stefan Berger 67b7b5dd3b tests: Add -traditional to OpenSSL command line when OSSL v3 is used
OpenSSL 3 did not maintain backwards compatibility with the key format
when for example RSA keys with passwords are created and in this case
one has to add -traditional to the command line to get the key in the
old format.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-09-18 16:34:19 -04:00
Stefan Berger f888c8146f
Merge pull request #87 from Fatmylin/main
chore: fix readme typo
2022-08-19 08:39:01 -04:00
Jimmy Hsiao 98e43be321 chore: fix readme typo
Signed-off-by: Jimmy Hsiao <jimmy.h@incubit.co.jp>
2022-08-19 09:31:50 +08:00
Stefan Berger 9b2765d096
Merge pull request #88 from containerd/lumjjb-patch-1
Update to min golang 1.18
2022-08-18 07:58:17 -04:00
Brandon Lum 554ec9bc51
Update to min golang 1.18
github.com/containerd/containerd/runtime now requires 1.18 

Signed-off-by: Brandon Lum <lumjjb@gmail.com>
2022-08-17 21:47:01 -04:00
Stefan Berger 23f077229f
Merge pull request #85 from stefanberger/stefanberger/changes
CHANGES: Updated CHANGES document for 1.1.6 release
2022-06-07 19:23:03 -04:00
Stefan Berger ec7aae5445 CHANGES: Updated CHANGES document for 1.1.6 release
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-06-07 19:06:03 -04:00
Stefan Berger fd230421fa
Merge pull request #83 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.6.6
build(deps): bump github.com/containerd/containerd from 1.6.1 to 1.6.6
2022-06-06 21:15:35 -04:00
Stefan Berger 5e28802f75
Merge pull request #84 from stefanberger/stefanberger/upgrade_golangci-lint
CI: Upgrade to golangci-lint v1.46.2
2022-06-06 21:14:32 -04:00
Stefan Berger ef8596ecba CI: Upgrade to golangci-lint v1.46.2
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-06-06 21:07:11 -04:00
dependabot[bot] 5959e8cf6d
build(deps): bump github.com/containerd/containerd from 1.6.1 to 1.6.6
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.1 to 1.6.6.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.1...v1.6.6)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 22:09:02 +00:00
Stefan Berger 715ba8c513 Update to ocicrypt 1.1.5 to get yaml.v3
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-06-06 11:14:27 -04:00
Stefan Berger 4f79bd6774 CHANGES: Updated CHANGES document for 1.1.5 release
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-05-18 15:27:53 +02:00
Stefan Berger 4c38f1094b Bump ocicrypt to 1.1.4
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-05-18 15:27:53 +02:00
Stefan Berger 784a5a3c0d
Merge pull request #79 from stefanberger/stefanberger/adjust_ci_to_main
CICD: Rename master branch to main
2022-04-22 07:50:40 -04:00
Stefan Berger 8abd19d902 CICD: Rename master branch to main
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-04-21 21:26:01 -04:00
Stefan Berger 5bc5ba0953
Merge pull request #78 from kzys/any-rename
Rename any to pbAny
2022-04-21 20:51:13 -04:00
Kazuyoshi Kato 0e5d997041 Rename any to pbAny
Go 1.18 introduces any as an alias for the empty interface. While having
our own any doesn't confuse Go compiler, it would confuse humans.

Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
2022-04-21 23:36:11 +00:00
Kazuyoshi Kato cb14b455fe Test with Go 1.18
Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
2022-04-21 23:28:02 +00:00
Derek McGowan 8ba028dca0
Merge pull request #75 from kzys/imgcrypt-typeurl
Use reflect to support diff.ApplyConfig with/without gogo's types.Any
2022-04-20 21:46:38 -07:00
Kazuyoshi Kato 9f08722ade Use reflect to support diff.ApplyConfig with/without gogo's types.Any
containerd is migrating off from github.com/gogo/protobuf
(see https://github.com/containerd/containerd/issues/6564).

However imgcrypt depends containerd and containerd also depends
imgcrypt, which makes changing this signature complicated.

This change workarounds the issue by using Go's reflect package.

Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
2022-04-21 00:33:17 +00:00
Stefan Berger 0796ea171c
Merge pull request #76 from kzys/ci-fix
Upgrade golangci-lint-action and golangci-lint
2022-04-20 13:18:33 -04:00
Kazuyoshi Kato 6eaeb4a586 Add build tags to make gofmt happy
Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
2022-04-20 17:04:13 +00:00
Kazuyoshi Kato 9cba55fc43 Upgrade golangci-lint-action and golangci-lint
Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
2022-04-20 17:01:45 +00:00
Stefan Berger 4b7120856c
Merge pull request #74 from stefanberger/changes
CHANGES: Updated CHANGES document for 1.1.4 release
2022-03-24 12:05:49 -04:00
Stefan Berger f5766549f0 CHANGES: Updated CHANGES document for 1.1.4 release
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
2022-03-24 11:50:59 -04:00
Stefan Berger b47494c374
Merge pull request #73 from stefanberger/to-containerd-1.6.1
Bump github.com/containerd/containerd from 1.5.10 to 1.6.1
2022-03-24 07:18:13 -04:00
Stefan Berger 2efa871163 Bump github.com/containerd/containerd from 1.5.10 to 1.6.1
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-03-22 17:19:51 -04:00
Stefan Berger 7eff50ecc4
Merge pull request #72 from kzys/typeurl
images: prepare for typeurl.Any
2022-03-22 17:03:45 -04:00
Kazuyoshi Kato f842da4603 images: prepare for typeurl.Any
typeurl.MarshalAny returns typeurl.Any since
https://github.com/containerd/typeurl/pull/32.

Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
2022-03-22 16:12:31 +00:00
Stefan Berger 6fdd9818a4 images: Add list of Platforms to CheckAuthorization()
To be able to properly perform an authorization check on an image we need
to know the platform to perform check when in cryptManifestList(). Extend
the logic for cryptoOp == cryptoOpUnwrapOnly to skip over manifests that
do not correspond to the local platform and return an error if no manifest
was found that matches the local platform.

The following projects seem NOT to be affect due to the change in the code
path of CheckAuthorization() since they are not using it:

- cri-o
- nerdctl
- skopeo
- buildah
- podman

The impact on imgcrypt via ctr-enc is not so clear either since
CheckAuthorization() is not called on the server side but by the ctr-enc
client, thus can be modified easily.

Resolves: https://github.com/containerd/imgcrypt/issues/69
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-03-21 09:52:07 -04:00
Stefan Berger f4400580b6 test: Test running of encrypted image only pulled for local platform
Create a reproducing test case for issue #69 by adding a test case
with a bash image that is only pulled for the local platform, so
without --all-platforms. The test case will likey work on amd64 but
does fail locally on a ppc64 host.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-03-21 09:52:07 -04:00
Stefan Berger bc7d7f2da8
Merge pull request #71 from stefanberger/update_ocicrypt
Bump ocicrypt to 1.1.3
2022-03-18 13:38:52 -04:00
Stefan Berger d4d468487c Bump ocicrypt to 1.1.3
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2022-03-18 12:13:07 -04:00
dependabot[bot] 727850ffb1 Bump github.com/containerd/containerd from 1.5.9 to 1.5.10
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.9 to 1.5.10.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.9...v1.5.10)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-03 11:04:45 -05:00
Akihiro Suda ebe9f238db
Merge pull request #67 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.5.9
Bump github.com/containerd/containerd from 1.5.8 to 1.5.9
2022-01-07 14:52:33 +09:00
dependabot[bot] 3c7db10f64
Bump github.com/containerd/containerd from 1.5.8 to 1.5.9
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.8 to 1.5.9.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.8...v1.5.9)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-06 17:49:50 +00:00
Stefan Berger 65d749cef3
Merge pull request #64 from stefanberger/stefanberger/changes
CHANGES: Updated CHANGES document for 1.1.3 release
2021-12-14 15:22:10 -05:00
Stefan Berger 3be33eb397 CHANGES: Updated CHANGES document for 1.1.3 release
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
2021-12-14 15:12:42 -05:00
Stefan Berger 6b38b73448
Merge pull request #63 from samuelkarp/project-main
docs: update project branch to main
2021-12-13 22:01:08 -05:00
Samuel Karp f193f83033
docs: update project branch to main
The https://github.com/containerd/project repository changed its default
branch from master to main.  This commit updates the references to that
project repository in the README.md and MAINTAINERS files.

Signed-off-by: Samuel Karp <skarp@amazon.com>
2021-12-13 17:38:25 -08:00
Stefan Berger 3d565b5c08
Merge pull request #61 from containerd/update_golangci
Update linter to match containerd repo
2021-11-19 11:48:23 -05:00
Brandon Lum f442a20718 Update linter to match containerd repo
Signed-off-by: Brandon Lum <lumjjb@gmail.com>
2021-11-19 09:48:18 -05:00
Brandon Lum 08bdc5d7d5 update CI golang version
Signed-off-by: Brandon Lum <lumjjb@gmail.com>
2021-11-18 20:01:44 +00:00
Stefan Berger 756684aa63
Merge pull request #59 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.5.8
Bump github.com/containerd/containerd from 1.5.7 to 1.5.8
2021-11-18 13:55:19 -05:00
dependabot[bot] 519a38f88a
Bump github.com/containerd/containerd from 1.5.7 to 1.5.8
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.7 to 1.5.8.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.7...v1.5.8)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-18 16:43:42 +00:00
Stefan Berger 0ae2c06b8d
Merge pull request #57 from stefanberger/stefanberger/update_to_ocicrypt_1.1.2
maint: Update to ocicrypt v1.1.2
2021-10-29 10:12:50 -04:00
Stefan Berger f3d97aff00 maint: Update to ocicrypt v1.1.2
Update to ocicrypt v1.1.2. Not much seems to have changed there code-wise.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2021-10-29 10:06:44 -04:00
Stefan Berger 902158d48d
Merge pull request #56 from AkihiroSuda/split-create-crypto-config
Decouple CreateCryptoConfig() from github.com/urfave/cli
2021-10-29 07:25:21 -04:00
Akihiro Suda fe5e256b4c
Decouple CreateCryptoConfig() from github.com/urfave/cli
Decouple `CreateCryptoConfig()` from `github.com/urfave/cli`, so that it
can be called from other applications that do not use `github.com/urfave/cli`.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2021-10-29 15:10:23 +09:00
Stefan Berger 527ba24dfb
Merge pull request #55 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.5.7
Bump github.com/containerd/containerd from 1.5.5 to 1.5.7
2021-10-04 16:55:39 -04:00
dependabot[bot] 5ad007c93b
Bump github.com/containerd/containerd from 1.5.5 to 1.5.7
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.5 to 1.5.7.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.5...v1.5.7)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-04 20:45:44 +00:00
zounengren 967ee1fc62 replace pkg/errors and bump related library
Signed-off-by: Zou Nengren <zouyee1989@gmail.com>
2021-09-22 17:23:25 -04:00
Stefan Berger be31719b51
Merge pull request #53 from sameo/topic/README
README: Fix CRI decryption document URL
2021-07-28 23:42:51 -04:00
Phil Estes e36f8e5ec3
Merge pull request #52 from containerd/dependabot/go_modules/github.com/containerd/containerd-1.5.4
Bump github.com/containerd/containerd from 1.5.2 to 1.5.4
2021-07-26 18:26:17 -04:00
dependabot[bot] 5945d46048
Bump github.com/containerd/containerd from 1.5.2 to 1.5.4
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.2 to 1.5.4.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.2...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-26 21:37:04 +00:00
Samuel Ortiz a852ef447c README: Fix CRI decryption document URL
It is now living under docs/cri/.

Signed-off-by: Samuel Ortiz <samuel.e.ortiz@protonmail.com>
2021-07-20 06:23:58 +02:00
Stefan Berger a223e5cd51
Merge pull request #51 from aledbf/1.5.2
Bump containerd to 1.5.2
2021-07-09 17:11:34 -04:00
Manuel Alejandro de Brito Fontes 1f947ef139
Bump containerd to 1.5.2
Signed-off-by: Manuel Alejandro de Brito Fontes <aledbf@gmail.com>
2021-07-09 16:54:14 -04:00
Stefan Berger 8d466d74fa
Merge pull request #49 from stefanberger/stefanberger/layerconvertfunc
images: Implement ConvertFunc for image en- and decryption
2021-05-13 12:51:42 -04:00
Stefan Berger b5e4c03ff5 images: Implement ConvertFunc for image en- and decryption
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2021-05-13 11:03:48 -04:00
Mike Brown 8eebb5ea37
Merge pull request #48 from containerd/make_release
Remove ctr-enc from installation
2021-05-13 09:24:02 -05:00
Brandon Lum cad55c90dc Remove ctr-enc from installation
Signed-off-by: Brandon Lum <lumjjb@gmail.com>
2021-05-13 10:16:16 -04:00
Stefan Berger f10de5d987
Merge pull request #47 from mikebrow/sync-with-containerd.1.5
vendor sync up with containerd 1.5 ga, and runc94
2021-05-10 18:01:42 -04:00
Mike Brown 1dea30ca25 sync up with containerd 1.5 ga, and runc94
Signed-off-by: Mike Brown <brownwm@us.ibm.com>
2021-05-10 16:53:12 -05:00
Stefan Berger 5ea063ab69
Merge pull request #46 from stefanberger/stefanberger/sync-ctr-containerd-1.5.0
Sync ctr-enc with containerd's ctr v1.5.0-rc.3
2021-05-08 19:22:14 -04:00
Stefan Berger 3712273fd4 CICD: Run 'apt update' before pulling packages
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2021-05-07 22:29:02 -04:00
Stefan Berger 99d39a563b ctr-enc: Set the version for ctr-enc when linking
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2021-05-07 21:03:20 -04:00
Stefan Berger 5c4f3ee7f4 Sync ctr-enc with containerd's ctr v1.5.0-rc.3
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2021-05-07 20:32:42 -04:00
Stefan Berger f385f6ca97
Merge pull request #44 from stefanberger/stefanberger/changes
Adding CHANGES document with entries for 1.1.0 and 1.1.1
2021-04-13 16:16:25 -04:00
Stefan Berger f2f175ef29 Adding CHANGES document with entries for 1.1.0 and 1.1.1
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2021-04-13 16:06:09 -04:00
Stefan Berger 0bed51b952
Merge pull request #43 from thaJeztah/update_deps
go.mod: update dependencies
2021-04-12 14:11:26 -04:00
Sebastiaan van Stijn 474e2bc66c
go.mod: update dependencies
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-04-12 19:37:17 +02:00
Sebastiaan van Stijn 7ed62a5278 go.mod: github.com/containerd/containerd v1.5.0-beta.3
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-12 11:16:19 -05:00
Stefan Berger eae98c98e8
Merge pull request #40 from containerd/nokp_test
Add imgcrypt test with invalid keyprovider path
2021-03-08 21:19:39 -05:00
Brandon Lum 97185d475c Add imgcrypt test with invalid keyprovider path
Signed-off-by: Brandon Lum <lumjjb@gmail.com>
2021-03-08 16:06:44 -05:00
2125 changed files with 2564 additions and 636491 deletions

View File

@ -2,21 +2,21 @@ name: CI
on:
push:
branches: [ master ]
branches: [ main ]
pull_request:
branches: [ master ]
branches: [ main ]
jobs:
checks:
name: Project Checks
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
timeout-minutes: 5
steps:
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: 1.16
go-version: 1.23
- name: Set env
shell: bash
@ -24,12 +24,12 @@ jobs:
echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV
echo "${{ github.workspace }}/bin" >> $GITHUB_PATH
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
path: src/github.com/containerd/imgcrypt
fetch-depth: 25
- uses: containerd/project-checks@v1
- uses: containerd/project-checks@v1.2.2
with:
working-directory: src/github.com/containerd/imgcrypt
@ -40,11 +40,10 @@ jobs:
strategy:
matrix:
go-version: [1.16]
os: [ubuntu-18.04]
os: [ubuntu-22.04]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
path: src/github.com/containerd/imgcrypt
@ -54,30 +53,36 @@ jobs:
echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV
echo "${{ github.workspace }}/bin" >> $GITHUB_PATH
- uses: golangci/golangci-lint-action@v2
- uses: golangci/golangci-lint-action@v3
with:
version: v1.29
version: v1.62.2
working-directory: src/github.com/containerd/imgcrypt
args: --timeout 120s
tests:
name: Linux Tests
runs-on: ubuntu-18.04
strategy:
matrix:
go: ["1.24", "1.23"]
os: [ubuntu-22.04, windows-2022]
name: Tests / ${{ matrix.os }} / ${{ matrix.go }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
needs: [linters, checks]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
repository: containerd/containerd
path: src/github.com/containerd/containerd
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
path: src/github.com/containerd/imgcrypt
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: 1.16
go-version: ${{ matrix.go }}
- name: Set env
shell: bash
@ -85,19 +90,30 @@ jobs:
echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV
echo "${{ github.workspace }}/bin" >> $GITHUB_PATH
- name: Tests
run: |
make test
make
working-directory: src/github.com/containerd/imgcrypt
- name: Dependencies
shell: bash
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
run: |
sudo apt install gnutls-bin softhsm2 libseccomp-dev
sudo apt-get update
sudo apt-get install -y gnutls-bin softhsm2 libseccomp-dev libbtrfs-dev
make binaries
sudo make install
sudo rm /usr/local/bin/ctr
mkdir ../../lumjjb && pushd ../../lumjjb
git clone https://github.com/lumjjb/simple-ocicrypt-keyprovider && cd simple-ocicrypt-keyprovider
make
sudo cp simple_crypt /usr/local/bin
popd
RUNC_COMMIT=$(grep opencontainers/runc go.mod | awk '{print $2}')
RUNC_COMMIT=v1.1.9
pushd ../..
rm -fR opencontainers/runc && mkdir -p opencontainers && cd opencontainers
git clone https://github.com/opencontainers/runc.git && cd runc
@ -107,34 +123,9 @@ jobs:
popd
working-directory: src/github.com/containerd/containerd
- run: |
make test
make
- name: Integration Tests
shell: bash
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
run: |
CONTAINERD=$(type -P containerd) KEYPROVIDER=/usr/local/bin/simple_crypt ./script/tests/test_encryption.sh
working-directory: src/github.com/containerd/imgcrypt
windows-tests:
name: Windows Tests
runs-on: windows-2019
timeout-minutes: 15
needs: [linters, checks]
steps:
- uses: actions/checkout@v2
with:
path: src/github.com/containerd/imgcrypt
- uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Set env
shell: bash
run: |
echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV
echo "${{ github.workspace }}/bin" >> $GITHUB_PATH
- run: |
make test
make
working-directory: src/github.com/containerd/imgcrypt

73
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,73 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '16 11 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

3
.gitignore vendored
View File

@ -1,3 +1,2 @@
*~
/ctr
/ctd-decoder
/bin/

View File

@ -1,20 +1,31 @@
linters:
enable:
- structcheck
- varcheck
- depguard
- staticcheck
- unconvert
- gofmt
- goimports
- golint
- govet
- revive
- ineffassign
- vet
- unused
- misspell
run:
skip-dirs:
issue:
exclude-dirs:
- cmd/ctr/commands/run
- cmd/ctr/commands/images
- cmd\\ctr\\commands\\run
- cmd\\ctr\\commands\\images
exclude-files:
- cmd/ctr/commands/commands.go
- cmd\\ctr\\commands\\commands.go
linters-settings:
depguard:
rules:
main:
files:
- $all
deny:
- pkg: "io/ioutil"

69
CHANGES Normal file
View File

@ -0,0 +1,69 @@
CHANGES
v1.2.0-rc1:
- Updated to ocicrypt v1.2.0
- Updated to containerd v2.0.0-rc.3
- Updated other dependencies
- Tests: Fixes to work with later version of containerd
v1.1.10:
- Updated to ocicrypt v1.1.10
- Added test cases with JKW EC key and added 2 more RSA keys
- Sync'ed enc-ctr with ctr of containerd v1.6.30
- Updated dependencies
v1.1.9:
- Updated to ocicrypt v1.1.9
- Updated dependencies
v1.1.8:
- Updated to containerd v1.6.23
- Sync'ed enc-ctr with ctr of containerd v1.6.23
- Updated to ocicrypt v1.1.8
v1.1.7:
- Added support for zstd-compressed layers
- Update to ocicrypt v1.1.6 for zstd-related dependencies
- Update to containerd v1.6.8
- Sync'ed ctr-enc with upstream ctr changes to import command
- Add support for --all-platforms to encrypt command of ctr-enc
v1.1.6:
- Update to ocicrypt v1.1.5 for yaml v3.0 dependency
- Update to containerd v1.6.6 for runc v1.1.2 dependency
v1.1.5:
- Update to ocicrypt v1.1.4; sha256 is the default now for padding in OAEP
for pkcs11; Set OCICRYPT_OAEP_HASHALG=sha1 environment variable to force
sha1 usage, which is required for example for SoftHSM 2.6.1.
v1.1.4:
- Fixed issue in CheckAuthorization() callpath for images with a ManifestList
- CVE-2022-24778
- Fix: https://github.com/containerd/imgcrypt/commit/6fdd9818a4d8142107b7ecd767d839c9707700d9
- Added test case covering this
- Updated to ocicrypt 1.1.3
- Updated to containerd 1.6.1
v1.1.3:
- Release v1.1.3 addresses issue #62 due to re-tagging of v1.1.2
- docs: update referenced containerd project branch to main
- Update linter to match containerd repo
- Update CI golang version
- Updated to containerd 1.5.8
v1.1.2:
- Decouple CreateCryptoConfig() from github.com/urfave/cli
- Updated to containerd 1.5.7
- Implemented ConvertFunc for image en- and decryption
- Replace pkg/errors with errors package
- Updated to ocicrypt 1.1.2
- Sync'ed ctr-enc with ctr of containerd-1.5.0
v1.1.1:
- rebased on ocicrypt 1.1.1
v1.1.0:
- rebased on ocicrypt 1.1.0
- added pkcs11 support; experimental
- added keyprovider support

View File

@ -1,7 +1,7 @@
# imgcrypt maintainers
#
# As a containerd sub-project, containerd maintainers are also included from https://github.com/containerd/project/blob/master/MAINTAINERS.
# See https://github.com/containerd/project/blob/master/GOVERNANCE.md for description of maintainer role
# As a containerd sub-project, containerd maintainers are also included from https://github.com/containerd/project/blob/main/MAINTAINERS.
# See https://github.com/containerd/project/blob/main/GOVERNANCE.md for description of maintainer role
#
# MAINTAINERS
# GitHub ID, Name, Email address

View File

@ -16,9 +16,14 @@
# Base path used to install.
DESTDIR ?= /usr/local
VERSION=$(shell git describe --match 'v[0-9]*' --dirty='.m' --always)
CTR_LDFLAGS=-ldflags '-X github.com/containerd/containerd/v2/version.Version=$(VERSION)'
COMMANDS=ctd-decoder ctr-enc
RELEASE_COMMANDS=ctd-decoder
BINARIES=$(addprefix bin/,$(COMMANDS))
RELEASE_BINARIES=$(addprefix bin/,$(RELEASE_COMMANDS))
.PHONY: check build ctd-decoder
@ -29,10 +34,10 @@ build: $(BINARIES)
FORCE:
bin/ctd-decoder: cmd/ctd-decoder FORCE
go build -o $@ -v ./cmd/ctd-decoder/
cd cmd && go build -o ../$@ -v ./ctd-decoder/
bin/ctr-enc: cmd/ctr FORCE
go build -o $@ -v ./cmd/ctr/
cd cmd && go build -o ../$@ ${CTR_LDFLAGS} -v ./ctr/
check:
@echo "$@"
@ -44,6 +49,11 @@ install:
@mkdir -p $(DESTDIR)/bin
@install $(BINARIES) $(DESTDIR)/bin
containerd-release:
@echo "$@"
@mkdir -p $(DESTDIR)/bin
@install $(RELEASE_BINARIES) $(DESTDIR)/bin
uninstall:
@echo "$@"
@rm -f $(addprefix $(DESTDIR)/bin/,$(notdir $(BINARIES)))

View File

@ -2,16 +2,16 @@
Project `imgcrypt` is a non-core subproject of containerd.
The `imgcrypt` library provides API exensions for containerd to support encrypted container images and implements
The `imgcrypt` library provides API extensions for containerd to support encrypted container images and implements
the `ctd-decoder` command line tool for use by containerd to decrypt encrypted container images. An extended version
of containerd's `ctr` tool (`ctr-enc') with support for encrypting and decrypting container images is also provided.
of containerd's `ctr` tool (`ctr-enc`) with support for encrypting and decrypting container images is also provided.
`imgcrypt` relies on the [`ocicrypt`](https://github.com/containers/ocicrypt) library for crypto functions on image layers.
# Usage
`imgcrypt` requires containerd 1.3 or later. Containerd 1.4 or later is required when used with Kubernetes.
For configuration instructions for kubernetes, please consult the [CRI decryption document](https://github.com/containerd/containerd/blob/master/docs/decryption.md).
For configuration instructions for kubernetes, please consult the [CRI decryption document](https://github.com/containerd/containerd/blob/main/docs/cri/decryption.md).
Build and install `imgcrypt`:
@ -25,7 +25,8 @@ installation we use /tmp for directories. Also, we build containerd 1.3 from the
```
# cat config.toml
disable_plugins = ["cri"]
version = 2
disabled_plugins = ["io.containerd.grpc.v1.cri"]
root = "/tmp/var/lib/containerd"
state = "/tmp/run/containerd"
[grpc]
@ -37,6 +38,10 @@ state = "/tmp/run/containerd"
accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar+gzip"
path = "/usr/local/bin/ctd-decoder"
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.zstd"]
accepts = ["application/vnd.oci.image.layer.v1.tar+zstd+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar+zstd"
path = "/usr/local/bin/ctd-decoder"
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar"
@ -96,8 +101,8 @@ Hello World!
**imgcrypt** is a non-core containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View File

@ -18,14 +18,13 @@ package main
import (
b64 "encoding/base64"
"io/ioutil"
"errors"
"os"
"path/filepath"
"strings"
encconfig "github.com/containers/ocicrypt/config"
cryptUtils "github.com/containers/ocicrypt/utils"
"github.com/pkg/errors"
)
// getDecryptionKeys reads the keys from the given directory
@ -49,7 +48,7 @@ func getDecryptionKeys(keysPath string) (encconfig.CryptoConfig, error) {
return errors.New("Symbolic links not supported in decryption keys paths")
}
privateKey, err := ioutil.ReadFile(path)
privateKey, err := os.ReadFile(path)
if err != nil {
return err
}

View File

@ -21,13 +21,14 @@ import (
"io"
"os"
"github.com/containerd/imgcrypt"
"github.com/containerd/imgcrypt/images/encryption"
"github.com/containerd/typeurl"
"github.com/gogo/protobuf/proto"
"github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/containerd/typeurl/v2"
"github.com/containerd/imgcrypt/v2"
"github.com/containerd/imgcrypt/v2/images/encryption"
"github.com/containerd/containerd/v2/pkg/protobuf/proto"
"github.com/containerd/containerd/v2/pkg/protobuf/types"
"github.com/urfave/cli/v2"
)
var (
@ -40,7 +41,7 @@ func main() {
app.Usage = Usage
app.Action = run
app.Flags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "decryption-keys-path",
Usage: "Path to load decryption keys from. (optional)",
},
@ -69,17 +70,17 @@ func decrypt(ctx *cli.Context) error {
decCc := &payload.DecryptConfig
// TODO: If decryption key path is set, get additional keys to augment payload keys
if ctx.GlobalIsSet("decryption-keys-path") {
keyPathCc, err := getDecryptionKeys(ctx.GlobalString("decryption-keys-path"))
if ctx.IsSet("decryption-keys-path") {
keyPathCc, err := getDecryptionKeys(ctx.String("decryption-keys-path"))
if err != nil {
return errors.Wrap(err, "Unable to get decryption keys in provided key path")
return fmt.Errorf("unable to get decryption keys in provided key path: %w", err)
}
decCc = combineDecryptionConfigs(keyPathCc.DecryptConfig, &payload.DecryptConfig)
}
_, r, _, err := encryption.DecryptLayer(decCc, os.Stdin, payload.Descriptor, false)
if err != nil {
return errors.Wrapf(err, "call to DecryptLayer failed")
return fmt.Errorf("call to DecryptLayer failed: %w", err)
}
for {
@ -88,7 +89,7 @@ func decrypt(ctx *cli.Context) error {
if err == io.EOF {
break
}
return errors.Wrapf(err, "could not copy data")
return fmt.Errorf("could not copy data: %w", err)
}
}
return nil
@ -97,19 +98,19 @@ func decrypt(ctx *cli.Context) error {
func getPayload() (*imgcrypt.Payload, error) {
data, err := readPayload()
if err != nil {
return nil, errors.Wrap(err, "read payload")
return nil, fmt.Errorf("read payload: %w", err)
}
var any types.Any
if err := proto.Unmarshal(data, &any); err != nil {
return nil, errors.Wrapf(err, "could not proto.Unmarshal() decrypt data")
var anything types.Any
if err := proto.Unmarshal(data, &anything); err != nil {
return nil, fmt.Errorf("could not proto.Unmarshal() decrypt data: %w", err)
}
v, err := typeurl.UnmarshalAny(&any)
v, err := typeurl.UnmarshalAny(&anything)
if err != nil {
return nil, errors.Wrapf(err, "could not UnmarshalAny() the decrypt data")
return nil, fmt.Errorf("could not UnmarshalAny() the decrypt data: %w", err)
}
l, ok := v.(*imgcrypt.Payload)
if !ok {
return nil, errors.Errorf("unknown payload type %s", any.TypeUrl)
return nil, fmt.Errorf("unknown payload type %s", anything.TypeUrl)
}
return l, nil
}

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@ -19,7 +20,7 @@
package main
import (
"io/ioutil"
"io"
"os"
)
@ -28,5 +29,5 @@ const payloadFD = 3
func readPayload() ([]byte, error) {
f := os.NewFile(payloadFD, "configFd")
defer f.Close()
return ioutil.ReadAll(f)
return io.ReadAll(f)
}

View File

@ -1,3 +1,4 @@
//go:build windows
// +build windows
/*
@ -19,11 +20,11 @@
package main
import (
"io/ioutil"
"fmt"
"io"
"os"
winio "github.com/Microsoft/go-winio"
"github.com/pkg/errors"
)
func readPayload() ([]byte, error) {
@ -31,8 +32,8 @@ func readPayload() ([]byte, error) {
conn, err := winio.DialPipe(path, nil)
if err != nil {
return nil, errors.Wrapf(err, "could not DialPipe")
return nil, fmt.Errorf("could not DialPipe: %w", err)
}
defer conn.Close()
return ioutil.ReadAll(conn)
return io.ReadAll(conn)
}

View File

@ -18,34 +18,35 @@ package app
import (
"fmt"
"io/ioutil"
"io"
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/cmd/ctr/commands/events"
"github.com/containerd/containerd/cmd/ctr/commands/install"
"github.com/containerd/containerd/cmd/ctr/commands/leases"
namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces"
"github.com/containerd/containerd/cmd/ctr/commands/plugins"
"github.com/containerd/containerd/cmd/ctr/commands/pprof"
"github.com/containerd/containerd/cmd/ctr/commands/snapshots"
"github.com/containerd/containerd/cmd/ctr/commands/tasks"
versionCmd "github.com/containerd/containerd/cmd/ctr/commands/version"
"github.com/containerd/containerd/defaults"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/version"
"github.com/containerd/containerd/v2/cmd/ctr/commands/content"
"github.com/containerd/containerd/v2/cmd/ctr/commands/events"
"github.com/containerd/containerd/v2/cmd/ctr/commands/install"
"github.com/containerd/containerd/v2/cmd/ctr/commands/leases"
namespacesCmd "github.com/containerd/containerd/v2/cmd/ctr/commands/namespaces"
ociCmd "github.com/containerd/containerd/v2/cmd/ctr/commands/oci"
"github.com/containerd/containerd/v2/cmd/ctr/commands/plugins"
"github.com/containerd/containerd/v2/cmd/ctr/commands/pprof"
"github.com/containerd/containerd/v2/cmd/ctr/commands/snapshots"
"github.com/containerd/containerd/v2/cmd/ctr/commands/tasks"
versionCmd "github.com/containerd/containerd/v2/cmd/ctr/commands/version"
"github.com/containerd/containerd/v2/defaults"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/version"
"github.com/containerd/imgcrypt/cmd/ctr/commands/containers"
"github.com/containerd/imgcrypt/cmd/ctr/commands/images"
"github.com/containerd/imgcrypt/cmd/ctr/commands/run"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"google.golang.org/grpc/grpclog"
)
var extraCmds = []cli.Command{}
var extraCmds = []*cli.Command{}
func init() {
// Discard grpc logs so that they don't mess with our stdio
grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard))
grpclog.SetLoggerV2(grpclog.NewLoggerV2(io.Discard, io.Discard, io.Discard))
cli.VersionPrinter = func(c *cli.Context) {
fmt.Println(c.App.Name, version.Package, c.App.Version)
@ -57,6 +58,11 @@ func New() *cli.App {
app := cli.NewApp()
app.Name = "ctr"
app.Version = version.Version
app.Description = `
ctr is an unsupported debug and administrative client for interacting
with the containerd daemon. Because it is unsupported, the commands,
options, and operations are not guaranteed to be backward compatible or
stable from release to release of the containerd project.`
app.Usage = `
__
_____/ /______
@ -66,32 +72,37 @@ func New() *cli.App {
containerd CLI
`
app.DisableSliceFlagSeparator = true
app.EnableBashCompletion = true
app.Flags = []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
Usage: "enable debug output in logs",
},
cli.StringFlag{
Name: "address, a",
Usage: "address for containerd's GRPC server",
Value: defaults.DefaultAddress,
&cli.StringFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "address for containerd's GRPC server",
Value: defaults.DefaultAddress,
EnvVars: []string{"CONTAINERD_ADDRESS"},
},
cli.DurationFlag{
&cli.DurationFlag{
Name: "timeout",
Usage: "total timeout for ctr commands",
},
cli.DurationFlag{
&cli.DurationFlag{
Name: "connect-timeout",
Usage: "timeout for connecting to containerd",
},
cli.StringFlag{
Name: "namespace, n",
Usage: "namespace to use with commands",
Value: namespaces.Default,
EnvVar: namespaces.NamespaceEnvVar,
&cli.StringFlag{
Name: "namespace",
Aliases: []string{"n"},
Usage: "namespace to use with commands",
Value: namespaces.Default,
EnvVars: []string{namespaces.NamespaceEnvVar},
},
}
app.Commands = append([]cli.Command{
app.Commands = append([]*cli.Command{
plugins.Command,
versionCmd.Command,
containers.Command,
@ -105,9 +116,10 @@ containerd CLI
snapshots.Command,
tasks.Command,
install.Command,
ociCmd.Command,
}, extraCmds...)
app.Before = func(context *cli.Context) error {
if context.GlobalBool("debug") {
if context.Bool("debug") {
logrus.SetLevel(logrus.DebugLevel)
}
return nil

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@ -18,7 +19,7 @@
package app
import "github.com/containerd/containerd/cmd/ctr/commands/shim"
import "github.com/containerd/containerd/v2/cmd/ctr/commands/shim"
func init() {
extraCmds = append(extraCmds, shim.Command)

View File

@ -23,130 +23,9 @@ import (
"path/filepath"
"strings"
"github.com/containerd/containerd/defaults"
"github.com/urfave/cli"
)
"github.com/containerd/containerd/v2/pkg/atomicfile"
var (
// SnapshotterFlags are cli flags specifying snapshotter names
SnapshotterFlags = []cli.Flag{
cli.StringFlag{
Name: "snapshotter",
Usage: "snapshotter name. Empty value stands for the default value.",
EnvVar: "CONTAINERD_SNAPSHOTTER",
},
}
// LabelFlag is a cli flag specifying labels
LabelFlag = cli.StringSliceFlag{
Name: "label",
Usage: "labels to attach to the image",
}
// RegistryFlags are cli flags specifying registry options
RegistryFlags = []cli.Flag{
cli.BoolFlag{
Name: "skip-verify,k",
Usage: "skip SSL certificate validation",
},
cli.BoolFlag{
Name: "plain-http",
Usage: "allow connections using plain HTTP",
},
cli.StringFlag{
Name: "user,u",
Usage: "user[:password] Registry user and password",
},
cli.StringFlag{
Name: "refresh",
Usage: "refresh token for authorization server",
},
}
// ContainerFlags are cli flags specifying container options
ContainerFlags = []cli.Flag{
cli.StringFlag{
Name: "config,c",
Usage: "path to the runtime-specific spec config file",
},
cli.StringFlag{
Name: "cwd",
Usage: "specify the working directory of the process",
},
cli.StringSliceFlag{
Name: "env",
Usage: "specify additional container environment variables (i.e. FOO=bar)",
},
cli.StringSliceFlag{
Name: "label",
Usage: "specify additional labels (i.e. foo=bar)",
},
cli.StringSliceFlag{
Name: "mount",
Usage: "specify additional container mount (ex: type=bind,src=/tmp,dst=/host,options=rbind:ro)",
},
cli.BoolFlag{
Name: "net-host",
Usage: "enable host networking for the container",
},
cli.BoolFlag{
Name: "privileged",
Usage: "run privileged container",
},
cli.BoolFlag{
Name: "read-only",
Usage: "set the containers filesystem as readonly",
},
cli.StringFlag{
Name: "runtime",
Usage: "runtime name",
Value: defaults.DefaultRuntime,
},
cli.BoolFlag{
Name: "tty,t",
Usage: "allocate a TTY for the container",
},
cli.StringSliceFlag{
Name: "with-ns",
Usage: "specify existing Linux namespaces to join at container runtime (format '<nstype>:<path>')",
},
cli.StringFlag{
Name: "pid-file",
Usage: "file path to write the task's pid",
},
cli.IntFlag{
Name: "gpus",
Usage: "add gpus to the container",
},
cli.BoolFlag{
Name: "allow-new-privs",
Usage: "turn off OCI spec's NoNewPrivileges feature flag",
},
cli.Uint64Flag{
Name: "memory-limit",
Usage: "memory limit (in bytes) for the container",
},
cli.StringSliceFlag{
Name: "device",
Usage: "add a device to a container",
},
}
// ImageDecryptionFlags are cli flags needed when decrypting an image
ImageDecryptionFlags = []cli.Flag{
cli.StringFlag{
Name: "gpg-homedir",
Usage: "The GPG homedir to use; by default gpg uses ~/.gnupg",
}, cli.StringFlag{
Name: "gpg-version",
Usage: "The GPG version (\"v1\" or \"v2\"), default will make an educated guess",
}, cli.StringSliceFlag{
Name: "key",
Usage: "A secret key's filename and an optional password separated by colon; this option may be provided multiple times",
}, cli.StringSliceFlag{
Name: "dec-recipient",
Usage: "Recipient of the image; used only for PKCS7 and must be an x509 certificate",
},
}
"github.com/urfave/cli/v2"
)
// ObjectWithLabelArgs returns the first arg and a LabelArgs object
@ -176,6 +55,19 @@ func LabelArgs(labelStrings []string) map[string]string {
return labels
}
// AnnotationArgs returns a map of annotation key,value pairs.
func AnnotationArgs(annoStrings []string) (map[string]string, error) {
annotations := make(map[string]string, len(annoStrings))
for _, anno := range annoStrings {
parts := strings.SplitN(anno, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid key=value format annotation: %v", anno)
}
annotations[parts[0]] = parts[1]
}
return annotations, nil
}
// PrintAsJSON prints input in JSON format
func PrintAsJSON(x interface{}) {
b, err := json.MarshalIndent(x, "", " ")
@ -191,15 +83,14 @@ func WritePidFile(path string, pid int) error {
if err != nil {
return err
}
tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666)
f, err := atomicfile.New(path, 0o666)
if err != nil {
return err
}
_, err = fmt.Fprintf(f, "%d", pid)
f.Close()
if err != nil {
f.Cancel()
return err
}
return os.Rename(tempPath, path)
return f.Close()
}

View File

@ -17,13 +17,13 @@
package containers
import (
"errors"
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
"github.com/urfave/cli"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/errdefs"
"github.com/urfave/cli/v2"
)
var checkpointCommand = cli.Command{
@ -31,15 +31,15 @@ var checkpointCommand = cli.Command{
Usage: "checkpoint a container",
ArgsUsage: "CONTAINER REF",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "rw",
Usage: "include the rw layer in the checkpoint",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "image",
Usage: "include the image in the checkpoint",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "task",
Usage: "checkpoint container task",
},
@ -88,7 +88,7 @@ var checkpointCommand = cli.Command{
}
defer func() {
if err := task.Resume(ctx); err != nil {
fmt.Println(errors.Wrap(err, "error resuming task"))
fmt.Println(fmt.Errorf("error resuming task: %w", err))
}
}()
}

View File

@ -23,32 +23,31 @@ import (
"strings"
"text/tabwriter"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/errdefs"
"github.com/containerd/imgcrypt/cmd/ctr/commands/flags"
"github.com/containerd/imgcrypt/cmd/ctr/commands/run"
"github.com/containerd/typeurl"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/containerd/log"
"github.com/containerd/typeurl/v2"
"github.com/urfave/cli/v2"
)
// Command is the cli command for managing containers
var Command = cli.Command{
var Command = &cli.Command{
Name: "containers",
Usage: "manage containers",
Aliases: []string{"c", "container"},
Subcommands: []cli.Command{
createCommand,
deleteCommand,
infoCommand,
listCommand,
setLabelsCommand,
checkpointCommand,
restoreCommand,
Subcommands: []*cli.Command{
&createCommand,
&deleteCommand,
&infoCommand,
&listCommand,
&setLabelsCommand,
&checkpointCommand,
&restoreCommand,
},
}
@ -56,7 +55,7 @@ var createCommand = cli.Command{
Name: "create",
Usage: "create container",
ArgsUsage: "[flags] Image|RootFS CONTAINER [COMMAND] [ARG...]",
Flags: append(append(commands.SnapshotterFlags, commands.ContainerFlags...), flags.ImageDecryptionFlags...),
Flags: append(commands.RuntimeFlags, append(append(commands.SnapshotterFlags, commands.ContainerFlags...), flags.ImageDecryptionFlags...)...),
Action: func(context *cli.Context) error {
var (
id string
@ -67,17 +66,17 @@ var createCommand = cli.Command{
if config {
id = context.Args().First()
if context.NArg() > 1 {
return errors.Wrap(errdefs.ErrInvalidArgument, "with spec config file, only container id should be provided")
return fmt.Errorf("with spec config file, only container id should be provided: %w", errdefs.ErrInvalidArgument)
}
} else {
id = context.Args().Get(1)
ref = context.Args().First()
if ref == "" {
return errors.Wrap(errdefs.ErrInvalidArgument, "image ref must be provided")
return fmt.Errorf("image ref must be provided: %w", errdefs.ErrInvalidArgument)
}
}
if id == "" {
return errors.Wrap(errdefs.ErrInvalidArgument, "container id must be provided")
return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
@ -98,14 +97,15 @@ var listCommand = cli.Command{
Usage: "list containers",
ArgsUsage: "[flags] [<filter>, ...]",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the container id",
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "print only the container id",
},
},
Action: func(context *cli.Context) error {
var (
filters = context.Args()
filters = context.Args().Slice()
quiet = context.Bool("quiet")
)
client, ctx, cancel, err := commands.NewClient(context)
@ -150,9 +150,9 @@ var deleteCommand = cli.Command{
Name: "delete",
Usage: "delete one or more existing containers",
ArgsUsage: "[flags] CONTAINER [CONTAINER, ...]",
Aliases: []string{"del", "rm"},
Aliases: []string{"del", "remove", "rm"},
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "keep-snapshot",
Usage: "do not clean up snapshot with container",
},
@ -170,9 +170,9 @@ var deleteCommand = cli.Command{
}
if context.NArg() == 0 {
return errors.Wrap(errdefs.ErrInvalidArgument, "must specify at least one container to delete")
return fmt.Errorf("must specify at least one container to delete: %w", errdefs.ErrInvalidArgument)
}
for _, arg := range context.Args() {
for _, arg := range context.Args().Slice() {
if err := deleteContainer(ctx, client, arg, deleteOpts...); err != nil {
if exitErr == nil {
exitErr = err
@ -216,7 +216,7 @@ var setLabelsCommand = cli.Command{
Action: func(context *cli.Context) error {
containerID, labels := commands.ObjectWithLabelArgs(context)
if containerID == "" {
return errors.Wrap(errdefs.ErrInvalidArgument, "container id must be provided")
return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
@ -249,10 +249,16 @@ var infoCommand = cli.Command{
Name: "info",
Usage: "get info about a container",
ArgsUsage: "CONTAINER",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "spec",
Usage: "only display the spec",
},
},
Action: func(context *cli.Context) error {
id := context.Args().First()
if id == "" {
return errors.Wrap(errdefs.ErrInvalidArgument, "container id must be provided")
return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
@ -267,8 +273,16 @@ var infoCommand = cli.Command{
if err != nil {
return err
}
if context.Bool("spec") {
v, err := typeurl.UnmarshalAny(info.Spec)
if err != nil {
return err
}
commands.PrintAsJSON(v)
return nil
}
if info.Spec != nil && info.Spec.Value != nil {
if info.Spec != nil && info.Spec.GetValue() != nil {
v, err := typeurl.UnmarshalAny(info.Spec)
if err != nil {
return err

View File

@ -17,12 +17,13 @@
package containers
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
"github.com/urfave/cli"
"errors"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/errdefs"
"github.com/urfave/cli/v2"
)
var restoreCommand = cli.Command{
@ -30,11 +31,11 @@ var restoreCommand = cli.Command{
Usage: "restore a container from checkpoint",
ArgsUsage: "CONTAINER REF",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "rw",
Usage: "restore the rw layer from the checkpoint",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "live",
Usage: "restore the runtime and memory data from the checkpoint",
},

View File

@ -17,26 +17,26 @@
package flags
import (
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
// ImageDecryptionFlags are cli flags needed when decrypting an image
ImageDecryptionFlags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "gpg-homedir",
Usage: "The GPG homedir to use; by default gpg uses ~/.gnupg",
}, cli.StringFlag{
}, &cli.StringFlag{
Name: "gpg-version",
Usage: "The GPG version (\"v1\" or \"v2\"), default will make an educated guess",
}, cli.BoolFlag{
}, &cli.BoolFlag{
Name: "skip-decrypt-auth",
Usage: "Indicates if check authorization for use of images should be skipped i.e. for use in node key model",
},
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: "key",
Usage: "A secret key's filename and an optional password separated by colon; this option may be provided multiple times",
}, cli.StringSliceFlag{
}, &cli.StringSliceFlag{
Name: "dec-recipient",
Usage: "Recipient of the image; used only for PKCS7 and must be an x509 certificate",
},

View File

@ -0,0 +1,108 @@
/*
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 images
import (
"errors"
"fmt"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/images/converter"
"github.com/containerd/containerd/v2/core/images/converter/uncompress"
"github.com/containerd/platforms"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
)
var convertCommand = cli.Command{
Name: "convert",
Usage: "convert an image",
ArgsUsage: "[flags] <source_ref> <target_ref>",
Description: `Convert an image format.
e.g., 'ctr convert --uncompress --oci example.com/foo:orig example.com/foo:converted'
Use '--platform' to define the output platform.
When '--all-platforms' is given all images in a manifest list must be available.
`,
Flags: []cli.Flag{
// generic flags
&cli.BoolFlag{
Name: "uncompress",
Usage: "convert tar.gz layers to uncompressed tar layers",
},
&cli.BoolFlag{
Name: "oci",
Usage: "convert Docker media types to OCI media types",
},
// platform flags
&cli.StringSliceFlag{
Name: "platform",
Usage: "Pull content from a specific platform",
Value: &cli.StringSlice{},
},
&cli.BoolFlag{
Name: "all-platforms",
Usage: "exports content from all platforms",
},
},
Action: func(context *cli.Context) error {
var convertOpts []converter.Opt
srcRef := context.Args().Get(0)
targetRef := context.Args().Get(1)
if srcRef == "" || targetRef == "" {
return errors.New("src and target image need to be specified")
}
if !context.Bool("all-platforms") {
if pss := context.StringSlice("platform"); len(pss) > 0 {
var all []ocispec.Platform
for _, ps := range pss {
p, err := platforms.Parse(ps)
if err != nil {
return fmt.Errorf("invalid platform %q: %w", ps, err)
}
all = append(all, p)
}
convertOpts = append(convertOpts, converter.WithPlatform(platforms.Ordered(all...)))
} else {
convertOpts = append(convertOpts, converter.WithPlatform(platforms.DefaultStrict()))
}
}
if context.Bool("uncompress") {
convertOpts = append(convertOpts, converter.WithLayerConvertFunc(uncompress.LayerConvertFunc))
}
if context.Bool("oci") {
convertOpts = append(convertOpts, converter.WithDockerToOCI(true))
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
newImg, err := converter.Convert(ctx, client, targetRef, srcRef, convertOpts...)
if err != nil {
return err
}
fmt.Fprintln(context.App.Writer, newImg.Target.Digest.String())
return nil
},
}

View File

@ -21,12 +21,14 @@ import (
"strings"
"github.com/containerd/containerd"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/imgcrypt/cmd/ctr/commands/img"
imgenc "github.com/containerd/imgcrypt/images/encryption"
imgenc "github.com/containerd/imgcrypt/v2/images/encryption"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
"github.com/containerd/platforms"
encconfig "github.com/containers/ocicrypt/config"
"github.com/urfave/cli/v2"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@ -238,3 +240,13 @@ func parsePlatformArray(specifiers []string) ([]ocispec.Platform, error) {
}
return speclist, nil
}
func ParseEncArgs(context *cli.Context) parsehelpers.EncArgs {
return parsehelpers.EncArgs{
GPGHomedir: context.String("gpg-homedir"),
GPGVersion: context.String("gpg-version"),
Key: context.StringSlice("key"),
Recipient: context.StringSlice("recipient"),
DecRecipient: context.StringSlice("dec-recipient"),
}
}

View File

@ -17,13 +17,17 @@
package images
import (
"errors"
"fmt"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/imgcrypt/cmd/ctr/commands/flags"
imgenc "github.com/containerd/imgcrypt/images/encryption"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/containerd/imgcrypt/cmd/ctr/commands/img"
imgenc "github.com/containerd/imgcrypt/v2/images/encryption"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
"github.com/urfave/cli/v2"
)
var decryptCommand = cli.Command{
@ -34,7 +38,7 @@ var decryptCommand = cli.Command{
Decrypt an image using private keys.
The user has contol over which layers to decrypt and for which platform.
If no payers or platforms are specified, all layers for all platforms are
If no layers or platforms are specified, all layers for all platforms are
decrypted.
Private keys in PEM format may be encrypted and the password may be passed
@ -44,10 +48,10 @@ var decryptCommand = cli.Command{
- <filename>:fd=<file descriptor>
- <filename>:filename=<password file>
`,
Flags: append(append(commands.RegistryFlags, cli.IntSliceFlag{
Flags: append(append(commands.RegistryFlags, &cli.IntSliceFlag{
Name: "layer",
Usage: "The layer to decrypt; this must be either the layer number or a negative number starting with -1 for topmost layer",
}, cli.StringSliceFlag{
}, &cli.StringSliceFlag{
Name: "platform",
Usage: "For which platform to decrypt; by default decryption is done for all platforms",
},
@ -70,7 +74,7 @@ var decryptCommand = cli.Command{
}
defer cancel()
layers32 := commands.IntToInt32Array(context.IntSlice("layer"))
layers32 := img.IntToInt32Array(context.IntSlice("layer"))
_, descs, err := getImageLayerInfos(client, ctx, local, layers32, context.StringSlice("platform"))
if err != nil {
@ -83,7 +87,7 @@ var decryptCommand = cli.Command{
return nil
}
cc, err := CreateDecryptCryptoConfig(context, descs)
cc, err := parsehelpers.CreateDecryptCryptoConfig(ParseEncArgs(context), descs)
if err != nil {
return err
}

View File

@ -17,12 +17,16 @@
package images
import (
"errors"
"fmt"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/imgcrypt/cmd/ctr/commands/flags"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/containerd/imgcrypt/cmd/ctr/commands/img"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
"github.com/urfave/cli/v2"
)
var encryptCommand = cli.Command{
@ -31,10 +35,10 @@ var encryptCommand = cli.Command{
ArgsUsage: "[flags] <local> <new name>",
Description: `Encrypt an image locally.
Encrypt an image using public keys managed by GPG.
Encrypt an image using public keys.
The user must provide recpients who will be able to decrypt the image using
their GPG-managed private key. For this the user's GPG keyring must hold the public
keys of the recipients.
their RSA, EC, or GPG-managed private key. If GPG keys are used then the
user's GPG keyring must hold the public keys of the recipients.
The user has control over the individual layers and the platforms they are
associated with and can encrypt them separately. If no layers or platforms are
specified, all layers for all platforms will be encrypted.
@ -47,13 +51,16 @@ var encryptCommand = cli.Command{
- jwe:<public-key-file-path>
- pkcs7:<x509-file-path>
`,
Flags: append(append(commands.RegistryFlags, cli.StringSliceFlag{
Flags: append(append(commands.RegistryFlags, &cli.StringSliceFlag{
Name: "recipient",
Usage: "Recipient of the image is the person who can decrypt it in the form specified above (i.e. jwe:/path/to/key)",
}, cli.IntSliceFlag{
}, &cli.IntSliceFlag{
Name: "layer",
Usage: "The layer to encrypt; this must be either the layer number or a negative number starting with -1 for topmost layer",
}, cli.StringSliceFlag{
}, &cli.BoolFlag{
Name: "all-platforms",
Usage: "encrypt for all platforms; this is the default",
}, &cli.StringSliceFlag{
Name: "platform",
Usage: "For which platform to encrypt; by default encrytion is done for all platforms",
}), flags.ImageDecryptionFlags...),
@ -80,14 +87,14 @@ var encryptCommand = cli.Command{
return errors.New("no recipients given -- nothing to do")
}
layers32 := commands.IntToInt32Array(context.IntSlice("layer"))
layers32 := img.IntToInt32Array(context.IntSlice("layer"))
_, descs, err := getImageLayerInfos(client, ctx, local, layers32, context.StringSlice("platform"))
if err != nil {
return err
}
cc, err := CreateCryptoConfig(context, descs)
cc, err := parsehelpers.CreateCryptoConfig(ParseEncArgs(context), descs)
if err != nil {
return err
}

View File

@ -17,15 +17,16 @@
package images
import (
"errors"
"fmt"
"io"
"os"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/images/archive"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/images/archive"
"github.com/containerd/platforms"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var exportCommand = cli.Command{
@ -40,16 +41,20 @@ Use '--platform' to define the output platform.
When '--all-platforms' is given all images in a manifest list must be available.
`,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-manifest-json",
Usage: "do not add Docker compatible manifest.json to archive",
},
cli.StringSliceFlag{
&cli.BoolFlag{
Name: "skip-non-distributable",
Usage: "do not add non-distributable blobs such as Windows layers to archive",
},
&cli.StringSliceFlag{
Name: "platform",
Usage: "Pull content from a specific platform",
Value: &cli.StringSlice{},
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "all-platforms",
Usage: "exports content from all platforms",
},
@ -69,13 +74,13 @@ When '--all-platforms' is given all images in a manifest list must be available.
for _, ps := range pss {
p, err := platforms.Parse(ps)
if err != nil {
return errors.Wrapf(err, "invalid platform %q", ps)
return fmt.Errorf("invalid platform %q: %w", ps, err)
}
all = append(all, p)
}
exportOpts = append(exportOpts, archive.WithPlatform(platforms.Ordered(all...)))
} else {
exportOpts = append(exportOpts, archive.WithPlatform(platforms.Default()))
exportOpts = append(exportOpts, archive.WithPlatform(platforms.DefaultStrict()))
}
if context.Bool("all-platforms") {
@ -86,6 +91,10 @@ When '--all-platforms' is given all images in a manifest list must be available.
exportOpts = append(exportOpts, archive.WithSkipDockerManifest())
}
if context.Bool("skip-non-distributable") {
exportOpts = append(exportOpts, archive.WithSkipNonDistributableBlobs())
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
@ -103,7 +112,7 @@ When '--all-platforms' is given all images in a manifest list must be available.
} else {
w, err = os.Create(out)
if err != nil {
return nil
return err
}
}
defer w.Close()

View File

@ -17,40 +17,43 @@
package images
import (
"errors"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/pkg/progress"
"github.com/containerd/containerd/platforms"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/containerd/v2/pkg/progress"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/containerd/platforms"
"github.com/urfave/cli/v2"
)
// Command is the cli command for managing images
var Command = cli.Command{
var Command = &cli.Command{
Name: "images",
Aliases: []string{"image", "i"},
Usage: "manage images",
Subcommands: cli.Commands{
checkCommand,
exportCommand,
importCommand,
listCommand,
pullCommand,
pushCommand,
removeCommand,
tagCommand,
setLabelsCommand,
encryptCommand,
decryptCommand,
layerinfoCommand,
&checkCommand,
&exportCommand,
&importCommand,
&listCommand,
&mountCommand,
&unmountCommand,
&pullCommand,
&pushCommand,
&removeCommand,
&tagCommand,
&setLabelsCommand,
&convertCommand,
&encryptCommand,
&decryptCommand,
&layerinfoCommand,
},
}
@ -61,14 +64,15 @@ var listCommand = cli.Command{
ArgsUsage: "[flags] [<filter>, ...]",
Description: "list images registered with containerd",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the image refs",
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "print only the image refs",
},
},
Action: func(context *cli.Context) error {
var (
filters = context.Args()
filters = context.Args().Slice()
quiet = context.Bool("quiet")
)
client, ctx, cancel, err := commands.NewClient(context)
@ -82,7 +86,7 @@ var listCommand = cli.Command{
)
imageList, err := imageStore.List(ctx, filters...)
if err != nil {
return errors.Wrap(err, "failed to list images")
return fmt.Errorf("failed to list images: %w", err)
}
if quiet {
for _, image := range imageList {
@ -144,9 +148,10 @@ var setLabelsCommand = cli.Command{
ArgsUsage: "[flags] <name> [<key>=<value>, ...]",
Description: "set and clear labels for an image",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "replace-all, r",
Usage: "replace all labels",
&cli.BoolFlag{
Name: "replace-all",
Aliases: []string{"r"},
Usage: "replace all labels",
},
},
Action: func(context *cli.Context) error {
@ -199,29 +204,42 @@ var setLabelsCommand = cli.Command{
var checkCommand = cli.Command{
Name: "check",
Usage: "check that an image has all content available locally",
Usage: "check existing images to ensure all content is available locally",
ArgsUsage: "[flags] [<filter>, ...]",
Description: "check that an image has all content available locally",
Flags: commands.SnapshotterFlags,
Description: "check existing images to ensure all content is available locally",
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "print only the ready image refs (fully downloaded and unpacked)",
},
}, commands.SnapshotterFlags...),
Action: func(context *cli.Context) error {
var (
exitErr error
quiet = context.Bool("quiet")
)
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
var (
contentStore = client.ContentStore()
tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
)
fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSTATUS\tSIZE\tUNPACKED\t")
args := []string(context.Args())
var contentStore = client.ContentStore()
args := context.Args().Slice()
imageList, err := client.ListImages(ctx, args...)
if err != nil {
return errors.Wrap(err, "failed listing images")
return fmt.Errorf("failed listing images: %w", err)
}
if len(imageList) == 0 {
log.G(ctx).Debugf("no images found")
return exitErr
}
var tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
if !quiet {
fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSTATUS\tSIZE\tUNPACKED\t")
}
for _, image := range imageList {
@ -230,15 +248,17 @@ var checkCommand = cli.Command{
size string
requiredSize int64
presentSize int64
complete bool = true
)
available, required, present, missing, err := images.Check(ctx, contentStore, image.Target(), platforms.Default())
if err != nil {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to check %v", image.Name())
exitErr = fmt.Errorf("unable to check %v: %w", image.Name(), err)
}
log.G(ctx).WithError(err).Errorf("unable to check %v", image.Name())
status = "error"
complete = false
}
if status != "error" {
@ -252,6 +272,7 @@ var checkCommand = cli.Command{
if len(missing) > 0 {
status = "incomplete"
complete = false
}
if available {
@ -260,6 +281,7 @@ var checkCommand = cli.Command{
} else {
status = fmt.Sprintf("unavailable (%v/?)", len(present))
size = fmt.Sprintf("%v/?", progress.Bytes(presentSize))
complete = false
}
} else {
size = "-"
@ -268,33 +290,40 @@ var checkCommand = cli.Command{
unpacked, err := image.IsUnpacked(ctx, context.String("snapshotter"))
if err != nil {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to check unpack for %v", image.Name())
exitErr = fmt.Errorf("unable to check unpack for %v: %w", image.Name(), err)
}
log.G(ctx).WithError(err).Errorf("unable to check unpack for %v", image.Name())
}
fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t%v\t%t\n",
image.Name(),
image.Target().MediaType,
image.Target().Digest,
status,
size,
unpacked)
if !quiet {
fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t%v\t%t\n",
image.Name(),
image.Target().MediaType,
image.Target().Digest,
status,
size,
unpacked)
} else {
if complete {
fmt.Println(image.Name())
}
}
}
if !quiet {
tw.Flush()
}
tw.Flush()
return exitErr
},
}
var removeCommand = cli.Command{
Name: "remove",
Aliases: []string{"rm"},
Name: "delete",
Aliases: []string{"del", "remove", "rm"},
Usage: "remove one or more images by reference",
ArgsUsage: "[flags] <ref> [<ref>, ...]",
Description: "remove one or more images by reference",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "sync",
Usage: "Synchronously remove image and all associated resources",
},
@ -309,7 +338,7 @@ var removeCommand = cli.Command{
exitErr error
imageStore = client.ImageService()
)
for i, target := range context.Args() {
for i, target := range context.Args().Slice() {
var opts []images.DeleteOpt
if context.Bool("sync") && i == context.NArg()-1 {
opts = append(opts, images.SynchronousDelete())
@ -317,7 +346,7 @@ var removeCommand = cli.Command{
if err := imageStore.Delete(ctx, target, opts...); err != nil {
if !errdefs.IsNotFound(err) {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to delete %v", target)
exitErr = fmt.Errorf("unable to delete %v: %w", target, err)
}
log.G(ctx).WithError(err).Errorf("unable to delete %v", target)
continue

View File

@ -22,14 +22,17 @@ import (
"os"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/images/archive"
"github.com/containerd/containerd/log"
"github.com/containerd/imgcrypt"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/images/archive"
"github.com/containerd/imgcrypt/cmd/ctr/commands/flags"
"github.com/containerd/imgcrypt/images/encryption"
"github.com/urfave/cli"
"github.com/containerd/log"
"github.com/containerd/platforms"
"github.com/urfave/cli/v2"
"github.com/containerd/imgcrypt/v2"
"github.com/containerd/imgcrypt/v2/images/encryption"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
)
var importCommand = cli.Command{
@ -52,30 +55,42 @@ e.g.
If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadbeef", the command will create
"foo/bar:latest" and "foo/bar@sha256:deadbeef" images in the containerd store.
Import of an encrypted image requires the decryption key to be passed. Even though the image will not be
decrypted it is required that the user proofs to be in possession of one of the decryption keys needed for
decrypting the image later on.
`,
Flags: append(append([]cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "base-name",
Value: "",
Usage: "base image name for added images, when provided only images with this name prefix are imported",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "digests",
Usage: "whether to create digest images (default: false)",
},
cli.StringFlag{
&cli.BoolFlag{
Name: "skip-digest-for-named",
Usage: "skip applying --digests option to images named in the importing tar (use it in conjunction with --digests)",
},
&cli.StringFlag{
Name: "index-name",
Usage: "image name to keep index as, by default index is discarded",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "all-platforms",
Usage: "imports content for all platforms, false by default",
},
cli.BoolFlag{
&cli.StringFlag{
Name: "platform",
Usage: "imports content for specific platform",
},
&cli.BoolFlag{
Name: "no-unpack",
Usage: "skip unpacking the images, false by default",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "compress-blobs",
Usage: "compress uncompressed blobs when creating manifest (Docker format only)",
},
@ -83,8 +98,9 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
Action: func(context *cli.Context) error {
var (
in = context.Args().First()
opts []containerd.ImportOpt
in = context.Args().First()
opts []containerd.ImportOpt
platformMatcher platforms.MatchComparer
)
prefix := context.String("base-name")
@ -99,6 +115,12 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
if context.Bool("digests") {
opts = append(opts, containerd.WithDigestRef(archive.DigestTranslator(prefix)))
}
if context.Bool("skip-digest-for-named") {
if !context.Bool("digests") {
return fmt.Errorf("--skip-digest-for-named must be specified with --digests option")
}
opts = append(opts, containerd.WithSkipDigestRef(func(name string) bool { return name != "" }))
}
if idxName := context.String("index-name"); idxName != "" {
opts = append(opts, containerd.WithIndexName(idxName))
@ -108,6 +130,15 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
opts = append(opts, containerd.WithImportCompression())
}
if platform := context.String("platform"); platform != "" {
platSpec, err := platforms.Parse(platform)
if err != nil {
return err
}
platformMatcher = platforms.OnlyStrict(platSpec)
opts = append(opts, containerd.WithImportPlatform(platformMatcher))
}
opts = append(opts, containerd.WithAllPlatforms(context.Bool("all-platforms")))
client, ctx, cancel, err := commands.NewClient(context)
@ -135,7 +166,7 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
}
if !context.Bool("no-unpack") {
cc, err := CreateDecryptCryptoConfig(context, nil)
cc, err := parsehelpers.CreateDecryptCryptoConfig(ParseEncArgs(context), nil)
if err != nil {
return err
}
@ -147,8 +178,10 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
log.G(ctx).Debugf("unpacking %d images", len(imgs))
for _, img := range imgs {
// TODO: Allow configuration of the platform
image := containerd.NewImage(client, img)
if platformMatcher == nil { // if platform not specified use default.
platformMatcher = platforms.Default()
}
image := containerd.NewImageWithPlatform(client, img, platformMatcher)
// TODO: Show unpack status
fmt.Printf("unpacking %s (%s)...", img.Name, img.Target.Digest)

View File

@ -17,18 +17,21 @@
package images
import (
"errors"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/imgcrypt/cmd/ctr/commands/img"
"github.com/containerd/platforms"
"github.com/containers/ocicrypt"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
"github.com/urfave/cli/v2"
)
var layerinfoCommand = cli.Command{
@ -44,19 +47,19 @@ var layerinfoCommand = cli.Command{
layers or platforms are specified, infomration for all layers and all
platforms will be retrieved.
`,
Flags: append(commands.RegistryFlags, cli.IntSliceFlag{
Flags: append(commands.RegistryFlags, &cli.IntSliceFlag{
Name: "layer",
Usage: "The layer to get info for; this must be either the layer number or a negative number starting with -1 for topmost layer",
}, cli.StringSliceFlag{
}, &cli.StringSliceFlag{
Name: "platform",
Usage: "For which platform to get the layer info; by default info for all platforms is retrieved",
}, cli.StringFlag{
}, &cli.StringFlag{
Name: "gpg-homedir",
Usage: "The GPG homedir to use; by default gpg uses ~/.gnupg",
}, cli.StringFlag{
}, &cli.StringFlag{
Name: "gpg-version",
Usage: "The GPG version (\"v1\" or \"v2\"), default will make an educated guess",
}, cli.BoolFlag{
}, &cli.BoolFlag{
Name: "n",
Usage: "Do not resolve PGP key IDs to email addresses",
}),
@ -71,7 +74,7 @@ var layerinfoCommand = cli.Command{
}
defer cancel()
layers32 := commands.IntToInt32Array(context.IntSlice("layer"))
layers32 := img.IntToInt32Array(context.IntSlice("layer"))
LayerInfos, _, err := getImageLayerInfos(client, ctx, local, layers32, context.StringSlice("platform"))
if err != nil {
@ -84,7 +87,7 @@ var layerinfoCommand = cli.Command{
var gpgClient ocicrypt.GPGClient
if !context.Bool("n") {
// create a GPG client to resolve keyIds to names
gpgClient, _ = createGPGClient(context)
gpgClient, _ = parsehelpers.CreateGPGClient(ParseEncArgs(context))
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.AlignRight)

View File

@ -0,0 +1,143 @@
/*
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 images
import (
"fmt"
"time"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/leases"
"github.com/containerd/containerd/v2/core/mount"
"github.com/containerd/containerd/v2/defaults"
"github.com/containerd/errdefs"
"github.com/containerd/platforms"
"github.com/opencontainers/image-spec/identity"
"github.com/urfave/cli/v2"
)
var mountCommand = cli.Command{
Name: "mount",
Usage: "mount an image to a target path",
ArgsUsage: "[flags] <ref> <target>",
Description: `Mount an image rootfs to a specified path.
When you are done, use the unmount command.
`,
Flags: append(append(commands.RegistryFlags, append(commands.SnapshotterFlags, commands.LabelFlag)...),
&cli.BoolFlag{
Name: "rw",
Usage: "Enable write support on the mount",
},
&cli.StringFlag{
Name: "platform",
Usage: "Mount the image for the specified platform",
Value: platforms.DefaultString(),
},
),
Action: func(context *cli.Context) (retErr error) {
var (
ref = context.Args().First()
target = context.Args().Get(1)
)
if ref == "" {
return fmt.Errorf("please provide an image reference to mount")
}
if target == "" {
return fmt.Errorf("please provide a target path to mount to")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
snapshotter := context.String("snapshotter")
if snapshotter == "" {
snapshotter = defaults.DefaultSnapshotter
}
ctx, done, err := client.WithLease(ctx,
leases.WithID(target),
leases.WithExpiration(24*time.Hour),
leases.WithLabels(map[string]string{
"containerd.io/gc.ref.snapshot." + snapshotter: target,
}),
)
if err != nil && !errdefs.IsAlreadyExists(err) {
return err
}
defer func() {
if retErr != nil && done != nil {
done(ctx)
}
}()
ps := context.String("platform")
p, err := platforms.Parse(ps)
if err != nil {
return fmt.Errorf("unable to parse platform %s: %w", ps, err)
}
img, err := client.ImageService().Get(ctx, ref)
if err != nil {
return err
}
i := containerd.NewImageWithPlatform(client, img, platforms.Only(p))
if err := i.Unpack(ctx, snapshotter); err != nil {
return fmt.Errorf("error unpacking image: %w", err)
}
diffIDs, err := i.RootFS(ctx)
if err != nil {
return err
}
chainID := identity.ChainID(diffIDs).String()
fmt.Println(chainID)
s := client.SnapshotService(snapshotter)
var mounts []mount.Mount
if context.Bool("rw") {
mounts, err = s.Prepare(ctx, target, chainID)
} else {
mounts, err = s.View(ctx, target, chainID)
}
if err != nil {
if errdefs.IsAlreadyExists(err) {
mounts, err = s.Mounts(ctx, target)
}
if err != nil {
return err
}
}
if err := mount.All(mounts, target); err != nil {
if err := s.Remove(ctx, target); err != nil && !errdefs.IsNotFound(err) {
fmt.Fprintln(context.App.ErrWriter, "Error cleaning up snapshot after mount error:", err)
}
return err
}
fmt.Fprintln(context.App.Writer, target)
return nil
},
}

View File

@ -18,19 +18,23 @@ package images
import (
"fmt"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms"
"github.com/containerd/imgcrypt"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/cmd/ctr/commands/content"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/imgcrypt/cmd/ctr/commands/flags"
"github.com/containerd/imgcrypt/images/encryption"
"github.com/containerd/log"
"github.com/containerd/platforms"
"github.com/containerd/imgcrypt/v2"
"github.com/containerd/imgcrypt/v2/images/encryption"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
"github.com/opencontainers/image-spec/identity"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var pullCommand = cli.Command{
@ -47,14 +51,26 @@ command. As part of this process, we do the following:
3. Register metadata for the image.
`,
Flags: append(append(append(commands.RegistryFlags, append(commands.SnapshotterFlags, commands.LabelFlag)...),
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: "platform",
Usage: "Pull content from a specific platform",
Value: &cli.StringSlice{},
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "all-platforms",
Usage: "pull content from all platforms",
Usage: "pull content and metadata from all platforms",
},
&cli.BoolFlag{
Name: "all-metadata",
Usage: "Pull metadata for all platforms",
},
&cli.BoolFlag{
Name: "print-chainid",
Usage: "Print the resulting image's chain ID",
},
&cli.IntFlag{
Name: "max-concurrent-downloads",
Usage: "Set the max concurrent downloads for each pull",
},
), flags.ImageDecryptionFlags...,
),
@ -82,6 +98,7 @@ command. As part of this process, we do the following:
if err != nil {
return err
}
img, err := content.Fetch(ctx, client, ref, config)
if err != nil {
return err
@ -95,13 +112,13 @@ command. As part of this process, we do the following:
if context.Bool("all-platforms") {
p, err = images.Platforms(ctx, client.ContentStore(), img.Target)
if err != nil {
return errors.Wrap(err, "unable to resolve image platforms")
return fmt.Errorf("unable to resolve image platforms: %w", err)
}
} else {
for _, s := range context.StringSlice("platform") {
ps, err := platforms.Parse(s)
if err != nil {
return errors.Wrapf(err, "unable to parse platform %s", s)
return fmt.Errorf("unable to parse platform %s: %w", s, err)
}
p = append(p, ps)
}
@ -110,7 +127,7 @@ command. As part of this process, we do the following:
p = append(p, platforms.DefaultSpec())
}
cc, err := CreateDecryptCryptoConfig(context, nil)
cc, err := parsehelpers.CreateDecryptCryptoConfig(ParseEncArgs(context), nil)
if err != nil {
return err
}
@ -119,6 +136,7 @@ command. As part of this process, we do the following:
}
opts := encryption.WithUnpackConfigApplyOpts(encryption.WithDecryptedUnpack(&ltdd))
start := time.Now()
for _, platform := range p {
fmt.Printf("unpacking %s %s...\n", platforms.Format(platform), img.Target.Digest)
i := containerd.NewImageWithPlatform(client, img, platforms.Only(platform))
@ -126,9 +144,16 @@ command. As part of this process, we do the following:
if err != nil {
return err
}
if context.Bool("print-chainid") {
diffIDs, err := i.RootFS(ctx)
if err != nil {
return err
}
chainID := identity.ChainID(diffIDs).String()
fmt.Printf("image chain ID: %s\n", chainID)
}
}
fmt.Println("done")
fmt.Printf("done: %s\t\n", time.Since(start))
return nil
},
}

View File

@ -18,23 +18,26 @@ package images
import (
gocontext "context"
"errors"
"fmt"
"net/http/httptrace"
"os"
"sync"
"text/tabwriter"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/pkg/progress"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/cmd/ctr/commands/content"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/containerd/v2/core/remotes"
"github.com/containerd/containerd/v2/core/remotes/docker"
"github.com/containerd/containerd/v2/pkg/progress"
"github.com/containerd/log"
"github.com/containerd/platforms"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"golang.org/x/sync/errgroup"
)
@ -51,19 +54,29 @@ var pushCommand = cli.Command{
creating the associated configuration, and creating the manifest
which references those resources.
`,
Flags: append(commands.RegistryFlags, cli.StringFlag{
Flags: append(commands.RegistryFlags, &cli.StringFlag{
Name: "manifest",
Usage: "digest of manifest",
}, cli.StringFlag{
}, &cli.StringFlag{
Name: "manifest-type",
Usage: "media type of manifest digest",
Value: ocispec.MediaTypeImageManifest,
}, &cli.StringSliceFlag{
Name: "platform",
Usage: "push content from a specific platform",
Value: &cli.StringSlice{},
}, &cli.IntFlag{
Name: "max-concurrent-uploaded-layers",
Usage: "Set the max concurrent uploaded layers for each push",
}, &cli.BoolFlag{
Name: "allow-non-distributable-blobs",
Usage: "Allow pushing blobs that are marked as non-distributable",
}),
Action: func(context *cli.Context) error {
var (
ref = context.Args().First()
local = context.Args().Get(1)
debug = context.GlobalBool("debug")
debug = context.Bool("debug")
desc ocispec.Descriptor
)
if ref == "" {
@ -79,7 +92,7 @@ var pushCommand = cli.Command{
if manifest := context.String("manifest"); manifest != "" {
desc.Digest, err = digest.Parse(manifest)
if err != nil {
return errors.Wrap(err, "invalid manifest digest")
return fmt.Errorf("invalid manifest digest: %w", err)
}
desc.MediaType = context.String("manifest-type")
} else {
@ -88,11 +101,35 @@ var pushCommand = cli.Command{
}
img, err := client.ImageService().Get(ctx, local)
if err != nil {
return errors.Wrap(err, "unable to resolve image to manifest")
return fmt.Errorf("unable to resolve image to manifest: %w", err)
}
desc = img.Target
if pss := context.StringSlice("platform"); len(pss) == 1 {
p, err := platforms.Parse(pss[0])
if err != nil {
return fmt.Errorf("invalid platform %q: %w", pss[0], err)
}
cs := client.ContentStore()
if manifests, err := images.Children(ctx, cs, desc); err == nil && len(manifests) > 0 {
matcher := platforms.NewMatcher(p)
for _, manifest := range manifests {
if manifest.Platform != nil && matcher.Match(*manifest.Platform) {
if _, err := images.Children(ctx, cs, manifest); err != nil {
return fmt.Errorf("no matching manifest: %w", err)
}
desc = manifest
break
}
}
}
}
}
if context.Bool("http-trace") {
ctx = httptrace.WithClientTrace(ctx, commands.NewDebugClientTrace(ctx))
}
resolver, err := commands.GetResolver(ctx, context)
if err != nil {
return err
@ -110,14 +147,29 @@ var pushCommand = cli.Command{
log.G(ctx).WithField("image", ref).WithField("digest", desc.Digest).Debug("pushing")
jobHandler := images.HandlerFunc(func(ctx gocontext.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if !context.Bool("allow-non-distributable-blobs") && images.IsNonDistributable(desc.MediaType) {
return nil, nil
}
ongoing.add(remotes.MakeRefKey(ctx, desc))
return nil, nil
})
return client.Push(ctx, ref, desc,
handler := jobHandler
if !context.Bool("allow-non-distributable-blobs") {
handler = remotes.SkipNonDistributableBlobs(handler)
}
ropts := []containerd.RemoteOpt{
containerd.WithResolver(resolver),
containerd.WithImageHandler(jobHandler),
)
containerd.WithImageHandler(handler),
}
if context.IsSet("max-concurrent-uploaded-layers") {
mcu := context.Int("max-concurrent-uploaded-layers")
ropts = append(ropts, containerd.WithMaxConcurrentUploadedLayers(mcu))
}
return client.Push(ctx, ref, desc, ropts...)
})
// don't show progress if debug mode is set

View File

@ -19,9 +19,9 @@ package images
import (
"fmt"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/errdefs"
"github.com/urfave/cli"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/errdefs"
"github.com/urfave/cli/v2"
)
var tagCommand = cli.Command{
@ -30,7 +30,7 @@ var tagCommand = cli.Command{
ArgsUsage: "[flags] <source_ref> <target_ref> [<target_ref>, ...]",
Description: `Tag an image for use in containerd.`,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "force",
Usage: "force target_ref to be created, regardless if it already exists",
},
@ -64,7 +64,7 @@ var tagCommand = cli.Command{
return err
}
// Support multiple references for one command run
for _, targetRef := range context.Args()[1:] {
for _, targetRef := range context.Args().Slice()[1:] {
image.Name = targetRef
// Attempt to create the image first
if _, err = imageService.Create(ctx, image); err != nil {

View File

@ -0,0 +1,72 @@
/*
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 images
import (
"fmt"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/leases"
"github.com/containerd/containerd/v2/core/mount"
"github.com/containerd/errdefs"
"github.com/urfave/cli/v2"
)
var unmountCommand = cli.Command{
Name: "unmount",
Usage: "unmount the image from the target",
ArgsUsage: "[flags] <target>",
Description: "Unmount the image rootfs from the specified target.",
Flags: append(append(commands.RegistryFlags, append(commands.SnapshotterFlags, commands.LabelFlag)...),
&cli.BoolFlag{
Name: "rm",
Usage: "remove the snapshot after a successful unmount",
},
),
Action: func(context *cli.Context) error {
var (
target = context.Args().First()
)
if target == "" {
return fmt.Errorf("please provide a target path to unmount from")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
if err := mount.UnmountAll(target, 0); err != nil {
return err
}
if context.Bool("rm") {
snapshotter := context.String("snapshotter")
s := client.SnapshotService(snapshotter)
if err := client.LeasesService().Delete(ctx, leases.Lease{ID: target}); err != nil && !errdefs.IsNotFound(err) {
return fmt.Errorf("error deleting lease: %w", err)
}
if err := s.Remove(ctx, target); err != nil && !errdefs.IsNotFound(err) {
return fmt.Errorf("error removing snapshot: %w", err)
}
}
fmt.Fprintln(context.App.Writer, target)
return nil
},
}

View File

@ -18,14 +18,15 @@ package img
import (
"context"
"fmt"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/errdefs"
"github.com/containerd/platforms"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
encocispec "github.com/containers/ocicrypt/spec"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// GetImageLayerDescriptors gets the image layer Descriptors of an image; the array contains
@ -56,8 +57,9 @@ func GetImageLayerDescriptors(ctx context.Context, cs content.Store, desc ocispe
switch child.MediaType {
case images.MediaTypeDockerSchema2LayerGzip, images.MediaTypeDockerSchema2Layer,
ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayer,
encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerEnc:
ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayerZstd, ocispec.MediaTypeImageLayer,
encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerEnc,
encocispec.MediaTypeLayerZstdEnc:
tdesc := child
tdesc.Platform = platform
tmp = append(tmp, tdesc)
@ -71,9 +73,19 @@ func GetImageLayerDescriptors(ctx context.Context, cs content.Store, desc ocispe
lis = append(lis, tmp...)
}
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig, "application/vnd.in-toto+json":
default:
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "GetImageLayerDescriptors: unhandled media type %s", desc.MediaType)
return nil, fmt.Errorf("unhandled media type %s: %w", desc.MediaType, errdefs.ErrInvalidArgument)
}
return lis, nil
}
// IntToInt32Array converts an array of int's to int32's
func IntToInt32Array(in []int) []int32 {
var ret []int32
for _, v := range in {
ret = append(ret, int32(v))
}
return ret
}

View File

@ -17,23 +17,27 @@
package run
import (
"context"
gocontext "context"
"encoding/csv"
"errors"
"fmt"
"strings"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/tasks"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/oci"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/cmd/ctr/commands/tasks"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/pkg/cio"
clabels "github.com/containerd/containerd/v2/pkg/labels"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/pkg/oci"
gocni "github.com/containerd/go-cni"
"github.com/containerd/imgcrypt/cmd/ctr/commands/flags"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func withMounts(context *cli.Context) oci.SpecOpts {
@ -61,8 +65,8 @@ func parseMountFlag(m string) (specs.Mount, error) {
}
for _, field := range fields {
v := strings.Split(field, "=")
if len(v) != 2 {
v := strings.SplitN(field, "=", 2)
if len(v) < 2 {
return mount, fmt.Errorf("invalid mount specification: expected key=val")
}
@ -86,50 +90,59 @@ func parseMountFlag(m string) (specs.Mount, error) {
}
// Command runs a container
var Command = cli.Command{
Name: "run",
Usage: "run a container",
ArgsUsage: "[flags] Image|RootFS ID [COMMAND] [ARG...]",
SkipArgReorder: true,
var Command = &cli.Command{
Name: "run",
Usage: "run a container",
ArgsUsage: "[flags] Image|RootFS ID [COMMAND] [ARG...]",
Flags: append([]cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "rm",
Usage: "remove the container after running",
Usage: "remove the container after running, cannot be used with --detach",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "null-io",
Usage: "send all IO to /dev/null",
},
cli.StringFlag{
&cli.StringFlag{
Name: "log-uri",
Usage: "log uri",
},
cli.BoolFlag{
Name: "detach,d",
Usage: "detach from the task after it has started execution",
&cli.BoolFlag{
Name: "detach",
Aliases: []string{"d"},
Usage: "detach from the task after it has started execution, cannot be used with --rm",
},
cli.StringFlag{
&cli.StringFlag{
Name: "fifo-dir",
Usage: "directory used for storing IO FIFOs",
},
cli.StringFlag{
&cli.StringFlag{
Name: "cgroup",
Usage: "cgroup path (To disable use of cgroup, set to \"\" explicitly)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "platform",
Usage: "run image for specific platform",
},
}, append(platformRunFlags, append(commands.SnapshotterFlags, append(commands.ContainerFlags, flags.ImageDecryptionFlags...)...)...)...),
&cli.BoolFlag{
Name: "cni",
Usage: "enable cni networking for the container",
},
}, append(platformRunFlags,
append(commands.RuntimeFlags,
append(append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...),
commands.ContainerFlags...), flags.ImageDecryptionFlags...)...)...)...),
Action: func(context *cli.Context) error {
var (
err error
id string
ref string
tty = context.Bool("tty")
detach = context.Bool("detach")
config = context.IsSet("config")
rm = context.Bool("rm")
tty = context.Bool("tty")
detach = context.Bool("detach")
config = context.IsSet("config")
enableCNI = context.Bool("cni")
)
if config {
@ -148,6 +161,10 @@ var Command = cli.Command{
if id == "" {
return errors.New("container id must be provided")
}
if rm && detach {
return errors.New("flags --detach and --rm cannot be specified together")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
@ -157,7 +174,7 @@ var Command = cli.Command{
if err != nil {
return err
}
if context.Bool("rm") && !detach {
if rm && !detach {
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
}
var con console.Console
@ -168,15 +185,31 @@ var Command = cli.Command{
return err
}
}
var network gocni.CNI
if enableCNI {
if network, err = gocni.New(gocni.WithDefaultConf); err != nil {
return err
}
}
opts := getNewTaskOpts(context)
ioOpts := []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))}
task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), con, context.Bool("null-io"), context.String("log-uri"), ioOpts, opts...)
if err != nil {
return err
}
var statusC <-chan containerd.ExitStatus
if !detach {
defer task.Delete(ctx)
defer func() {
if enableCNI {
if err := network.Remove(ctx, fullID(ctx, container), ""); err != nil {
logrus.WithError(err).Error("network review")
}
}
task.Delete(ctx)
}()
if statusC, err = task.Wait(ctx); err != nil {
return err
}
@ -186,6 +219,16 @@ var Command = cli.Command{
return err
}
}
if enableCNI {
netNsPath, err := getNetNSPath(ctx, task)
if err != nil {
return err
}
if _, err := network.Setup(ctx, fullID(ctx, container), netNsPath); err != nil {
return err
}
}
if err := task.Start(ctx); err != nil {
return err
}
@ -214,3 +257,31 @@ var Command = cli.Command{
return nil
},
}
func fullID(ctx context.Context, c containerd.Container) string {
id := c.ID()
ns, ok := namespaces.Namespace(ctx)
if !ok {
return id
}
return fmt.Sprintf("%s-%s", ns, id)
}
// buildLabel builds the labels from command line labels and the image labels
func buildLabels(cmdLabels, imageLabels map[string]string) map[string]string {
labels := make(map[string]string)
for k, v := range imageLabels {
if err := clabels.Validate(k, v); err == nil {
labels[k] = v
} else {
// In case the image label is invalid, we output a warning and skip adding it to the
// container.
logrus.WithError(err).Warnf("unable to add image label with key %s to the container", k)
}
}
// labels from the command line will override image and the initial image config labels
for k, v := range cmdLabels {
labels[k] = v
}
return labels
}

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@ -20,23 +21,60 @@ package run
import (
gocontext "context"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/containerd/containerd"
"github.com/containerd/containerd/contrib/nvidia"
"github.com/containerd/containerd/oci"
"github.com/containerd/containerd/platforms"
"github.com/containerd/imgcrypt"
"github.com/containerd/containerd/api/types/runc/options"
runtimeoptions "github.com/containerd/containerd/api/types/runtimeoptions/v1"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/contrib/apparmor"
"github.com/containerd/containerd/v2/contrib/nvidia"
"github.com/containerd/containerd/v2/contrib/seccomp"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/core/snapshots"
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/platforms"
"github.com/containerd/imgcrypt/v2"
"github.com/containerd/imgcrypt/v2/images/encryption"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
"github.com/containerd/imgcrypt/cmd/ctr/commands"
"github.com/containerd/imgcrypt/cmd/ctr/commands/images"
"github.com/containerd/imgcrypt/images/encryption"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
var platformRunFlags []cli.Flag
var platformRunFlags = []cli.Flag{
&cli.StringFlag{
Name: "uidmap",
Usage: "run inside a user namespace with the specified UID mapping range; specified with the format `container-uid:host-uid:length`",
},
&cli.StringFlag{
Name: "gidmap",
Usage: "run inside a user namespace with the specified GID mapping range; specified with the format `container-gid:host-gid:length`",
},
&cli.BoolFlag{
Name: "remap-labels",
Usage: "provide the user namespace ID remapping to the snapshotter via label options; requires snapshotter support",
},
&cli.Float64Flag{
Name: "cpus",
Usage: "set the CFS cpu quota",
Value: 0.0,
},
&cli.IntFlag{
Name: "cpu-shares",
Usage: "set the cpu shares",
Value: 1024,
},
}
// NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
@ -56,16 +94,19 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
spec containerd.NewContainerOpts
)
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
if config {
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
opts = append(opts, oci.WithSpecFromFile(context.String("config")))
} else {
var (
ref = context.Args().First()
//for container's id is Args[1]
args = context.Args()[2:]
args = context.Args().Slice()[2:]
)
opts = append(opts, oci.WithDefaultSpec(), oci.WithDefaultUnixDevices)
if ef := context.String("env-file"); ef != "" {
opts = append(opts, oci.WithEnvFile(ef))
}
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
opts = append(opts, withMounts(context))
@ -75,6 +116,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
return nil, err
}
opts = append(opts, oci.WithRootFSPath(rootfs))
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
} else {
snapshotter := context.String("snapshotter")
var image containerd.Image
@ -96,9 +138,8 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if err != nil {
return nil, err
}
if !unpacked {
cc, err := images.CreateDecryptCryptoConfig(context, nil)
cc, err := parsehelpers.CreateDecryptCryptoConfig(images.ParseEncArgs(context), nil)
if err != nil {
return nil, err
}
@ -111,17 +152,45 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
return nil, err
}
}
labels := buildLabels(commands.LabelArgs(context.StringSlice("label")), image.Labels())
opts = append(opts, oci.WithImageConfig(image))
cOpts = append(cOpts,
containerd.WithImage(image),
containerd.WithSnapshotter(snapshotter),
// Even when "readonly" is set, we don't use KindView snapshot here. (#1495)
containerd.WithImageConfigLabels(image),
containerd.WithAdditionalContainerLabels(labels),
containerd.WithSnapshotter(snapshotter))
if uidmap, gidmap := context.String("uidmap"), context.String("gidmap"); uidmap != "" && gidmap != "" {
uidMap, err := parseIDMapping(uidmap)
if err != nil {
return nil, err
}
gidMap, err := parseIDMapping(gidmap)
if err != nil {
return nil, err
}
opts = append(opts,
oci.WithUserNamespace([]specs.LinuxIDMapping{uidMap}, []specs.LinuxIDMapping{gidMap}))
// use snapshotter opts or the remapped snapshot support to shift the filesystem
// currently the only snapshotter known to support the labels is fuse-overlayfs:
// https://github.com/AkihiroSuda/containerd-fuse-overlayfs
if context.Bool("remap-labels") {
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image,
containerd.WithRemapperLabels(0, uidMap.HostID, 0, gidMap.HostID, uidMap.Size)))
} else {
cOpts = append(cOpts, containerd.WithRemappedSnapshot(id, image, uidMap.HostID, gidMap.HostID))
}
} else {
// Even when "read-only" is set, we don't use KindView snapshot here. (#1495)
// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
// after creating some mount points on demand.
containerd.WithNewSnapshot(id, image),
containerd.WithImageStopSignal(image, "SIGTERM"))
// For some snapshotter, such as overlaybd, it can provide 2 kind of writable snapshot(overlayfs dir or block-device)
// by command label values.
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image,
snapshots.WithLabels(commands.LabelArgs(context.StringSlice("snapshotter-label")))))
}
cOpts = append(cOpts, containerd.WithImageStopSignal(image, "SIGTERM"))
}
if context.Bool("readonly") {
if context.Bool("read-only") {
opts = append(opts, oci.WithRootFSReadonly())
}
if len(args) > 0 {
@ -134,10 +203,90 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
opts = append(opts, oci.WithTTY)
}
if context.Bool("privileged") {
opts = append(opts, oci.WithPrivileged)
opts = append(opts, oci.WithPrivileged, oci.WithAllDevicesAllowed, oci.WithHostDevices)
}
if context.Bool("net-host") {
opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf)
hostname, err := os.Hostname()
if err != nil {
return nil, fmt.Errorf("get hostname: %w", err)
}
opts = append(opts,
oci.WithHostNamespace(specs.NetworkNamespace),
oci.WithHostHostsFile,
oci.WithHostResolvconf,
oci.WithEnv([]string{fmt.Sprintf("HOSTNAME=%s", hostname)}),
)
}
if annoStrings := context.StringSlice("annotation"); len(annoStrings) > 0 {
annos, err := commands.AnnotationArgs(annoStrings)
if err != nil {
return nil, err
}
opts = append(opts, oci.WithAnnotations(annos))
}
if caps := context.StringSlice("cap-add"); len(caps) > 0 {
for _, cap := range caps {
if !strings.HasPrefix(cap, "CAP_") {
return nil, fmt.Errorf("capabilities must be specified with 'CAP_' prefix")
}
}
opts = append(opts, oci.WithAddedCapabilities(caps))
}
if caps := context.StringSlice("cap-drop"); len(caps) > 0 {
for _, cap := range caps {
if !strings.HasPrefix(cap, "CAP_") {
return nil, fmt.Errorf("capabilities must be specified with 'CAP_' prefix")
}
}
opts = append(opts, oci.WithDroppedCapabilities(caps))
}
seccompProfile := context.String("seccomp-profile")
if !context.Bool("seccomp") && seccompProfile != "" {
return nil, fmt.Errorf("seccomp must be set to true, if using a custom seccomp-profile")
}
if context.Bool("seccomp") {
if seccompProfile != "" {
opts = append(opts, seccomp.WithProfile(seccompProfile))
} else {
opts = append(opts, seccomp.WithDefaultProfile())
}
}
if s := context.String("apparmor-default-profile"); len(s) > 0 {
opts = append(opts, apparmor.WithDefaultProfile(s))
}
if s := context.String("apparmor-profile"); len(s) > 0 {
if len(context.String("apparmor-default-profile")) > 0 {
return nil, fmt.Errorf("apparmor-profile conflicts with apparmor-default-profile")
}
opts = append(opts, apparmor.WithProfile(s))
}
if cpus := context.Float64("cpus"); cpus > 0.0 {
var (
period = uint64(100000)
quota = int64(cpus * 100000.0)
)
opts = append(opts, oci.WithCPUCFS(quota, period))
}
if shares := context.Int("cpu-shares"); shares > 0 {
opts = append(opts, oci.WithCPUShares(uint64(shares)))
}
quota := context.Int64("cpu-quota")
period := context.Uint64("cpu-period")
if quota != -1 || period != 0 {
if cpus := context.Float64("cpus"); cpus > 0.0 {
return nil, errors.New("cpus and quota/period should be used separately")
}
opts = append(opts, oci.WithCPUCFS(quota, period))
}
joinNs := context.StringSlice("with-ns")
@ -155,7 +304,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
}))
}
if context.IsSet("gpus") {
opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities))
opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.IntSlice("gpus")...), nvidia.WithAllCapabilities))
}
if context.IsSet("allow-new-privs") {
opts = append(opts, oci.WithNewPrivileges)
@ -169,11 +318,34 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
opts = append(opts, oci.WithMemoryLimit(limit))
}
for _, dev := range context.StringSlice("device") {
opts = append(opts, oci.WithLinuxDevice(dev, "rwm"))
opts = append(opts, oci.WithDevices(dev, "", "rwm"))
}
rootfsPropagation := context.String("rootfs-propagation")
if rootfsPropagation != "" {
opts = append(opts, func(_ gocontext.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error {
if s.Linux != nil {
s.Linux.RootfsPropagation = rootfsPropagation
} else {
s.Linux = &specs.Linux{
RootfsPropagation: rootfsPropagation,
}
}
return nil
})
}
if c := context.String("rdt-class"); c != "" {
opts = append(opts, oci.WithRdt(c, "", ""))
}
}
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
runtimeOpts, err := getRuntimeOptions(context)
if err != nil {
return nil, err
}
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), runtimeOpts))
opts = append(opts, oci.WithAnnotations(commands.LabelArgs(context.StringSlice("label"))))
var s specs.Spec
@ -181,7 +353,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts = append(cOpts, spec)
cc, err := images.CreateDecryptCryptoConfig(context, nil)
cc, err := parsehelpers.CreateDecryptCryptoConfig(images.ParseEncArgs(context), nil)
if err != nil {
return nil, err
}
@ -195,11 +367,91 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
return client.NewContainer(ctx, id, cOpts...)
}
func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
if context.Bool("no-pivot") {
return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
func getRuncOptions(context *cli.Context) (*options.Options, error) {
runtimeOpts := &options.Options{}
if runcBinary := context.String("runc-binary"); runcBinary != "" {
runtimeOpts.BinaryName = runcBinary
}
return nil
if context.Bool("runc-systemd-cgroup") {
if context.String("cgroup") == "" {
// runc maps "machine.slice:foo:deadbeef" to "/machine.slice/foo-deadbeef.scope"
return nil, errors.New("option --runc-systemd-cgroup requires --cgroup to be set, e.g. \"machine.slice:foo:deadbeef\"")
}
runtimeOpts.SystemdCgroup = true
}
if root := context.String("runc-root"); root != "" {
runtimeOpts.Root = root
}
return runtimeOpts, nil
}
func getRuntimeOptions(context *cli.Context) (interface{}, error) {
// validate first
if (context.String("runc-binary") != "" || context.Bool("runc-systemd-cgroup")) &&
context.String("runtime") != "io.containerd.runc.v2" {
return nil, errors.New("specifying runc-binary and runc-systemd-cgroup is only supported for \"io.containerd.runc.v2\" runtime")
}
if context.String("runtime") == "io.containerd.runc.v2" {
return getRuncOptions(context)
}
if configPath := context.String("runtime-config-path"); configPath != "" {
return &runtimeoptions.Options{
ConfigPath: configPath,
}, nil
}
return nil, nil
}
func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
var (
tOpts []containerd.NewTaskOpts
)
if context.Bool("no-pivot") {
tOpts = append(tOpts, containerd.WithNoPivotRoot)
}
if uidmap := context.String("uidmap"); uidmap != "" {
uidMap, err := parseIDMapping(uidmap)
if err != nil {
logrus.WithError(err).Warn("unable to parse uidmap; defaulting to uid 0 IO ownership")
}
tOpts = append(tOpts, containerd.WithUIDOwner(uidMap.HostID))
}
if gidmap := context.String("gidmap"); gidmap != "" {
gidMap, err := parseIDMapping(gidmap)
if err != nil {
logrus.WithError(err).Warn("unable to parse gidmap; defaulting to gid 0 IO ownership")
}
tOpts = append(tOpts, containerd.WithGIDOwner(gidMap.HostID))
}
return tOpts
}
func parseIDMapping(mapping string) (specs.LinuxIDMapping, error) {
parts := strings.Split(mapping, ":")
if len(parts) != 3 {
return specs.LinuxIDMapping{}, errors.New("user namespace mappings require the format `container-id:host-id:size`")
}
cID, err := strconv.ParseUint(parts[0], 0, 32)
if err != nil {
return specs.LinuxIDMapping{}, fmt.Errorf("invalid container id for user namespace remapping: %w", err)
}
hID, err := strconv.ParseUint(parts[1], 0, 32)
if err != nil {
return specs.LinuxIDMapping{}, fmt.Errorf("invalid host id for user namespace remapping: %w", err)
}
size, err := strconv.ParseUint(parts[2], 0, 32)
if err != nil {
return specs.LinuxIDMapping{}, fmt.Errorf("invalid size for user namespace remapping: %w", err)
}
return specs.LinuxIDMapping{
ContainerID: uint32(cID),
HostID: uint32(hID),
Size: uint32(size),
}, nil
}
func validNamespace(ns string) bool {
@ -217,3 +469,7 @@ func validNamespace(ns string) bool {
return false
}
}
func getNetNSPath(_ gocontext.Context, task containerd.Task) (string, error) {
return fmt.Sprintf("/proc/%d/ns/net", task.Pid()), nil
}

View File

@ -18,22 +18,26 @@ package run
import (
gocontext "context"
"errors"
"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/oci"
"github.com/containerd/imgcrypt"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/netns"
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/imgcrypt/cmd/ctr/commands"
"github.com/containerd/imgcrypt/cmd/ctr/commands/images"
"github.com/containerd/imgcrypt/images/encryption"
"github.com/containerd/imgcrypt/v2"
"github.com/containerd/imgcrypt/v2/images/encryption"
"github.com/containerd/imgcrypt/v2/images/encryption/parsehelpers"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var platformRunFlags = []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "isolated",
Usage: "run the container with vm isolation",
},
@ -53,10 +57,11 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if config {
id = context.Args().First()
opts = append(opts, oci.WithSpecFromFile(context.String("config")))
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
} else {
var (
ref = context.Args().First()
args = context.Args()[2:]
args = context.Args().Slice()[2:]
)
id = context.Args().Get(1)
@ -70,6 +75,9 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
opts = append(opts, oci.WithWindowNetworksAllowUnqualifiedDNSQuery())
opts = append(opts, oci.WithWindowsIgnoreFlushesDuringBoot())
}
if ef := context.String("env-file"); ef != "" {
opts = append(opts, oci.WithEnvFile(ef))
}
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
opts = append(opts, withMounts(context))
@ -82,7 +90,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
return nil, err
}
if !unpacked {
cc, err := images.CreateDecryptCryptoConfig(context, nil)
cc, err := parsehelpers.CreateDecryptCryptoConfig(images.ParseEncArgs(context), nil)
if err != nil {
return nil, err
}
@ -96,9 +104,13 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
}
}
opts = append(opts, oci.WithImageConfig(image))
cOpts = append(cOpts, containerd.WithImage(image))
cOpts = append(cOpts, containerd.WithSnapshotter(snapshotter))
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
labels := buildLabels(commands.LabelArgs(context.StringSlice("label")), image.Labels())
cOpts = append(cOpts,
containerd.WithImage(image),
containerd.WithImageConfigLabels(image),
containerd.WithSnapshotter(snapshotter),
containerd.WithNewSnapshot(id, image),
containerd.WithAdditionalContainerLabels(labels))
if len(args) > 0 {
opts = append(opts, oci.WithProcessArgs(args...))
@ -116,6 +128,16 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
}
opts = append(opts, oci.WithTTYSize(int(size.Width), int(size.Height)))
}
if context.Bool("net-host") {
return nil, errors.New("Cannot use host mode networking with Windows containers")
}
if context.Bool("cni") {
ns, err := netns.NewNetNS("")
if err != nil {
return nil, err
}
opts = append(opts, oci.WithWindowsNetworkNamespace(ns.GetPath()))
}
if context.Bool("isolated") {
opts = append(opts, oci.WithWindowsHyperV)
}
@ -129,12 +151,11 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
}
}
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
runtime := context.String("runtime")
var runtimeOpts interface{}
if runtime == "io.containerd.runhcs.v1" {
runtimeOpts = &options.Options{
Debug: context.GlobalBool("debug"),
Debug: context.Bool("debug"),
}
}
cOpts = append(cOpts, containerd.WithRuntime(runtime, runtimeOpts))
@ -144,7 +165,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts = append(cOpts, spec)
cc, err := images.CreateDecryptCryptoConfig(context, nil)
cc, err := parsehelpers.CreateDecryptCryptoConfig(images.ParseEncArgs(context), nil)
if err != nil {
return nil, err
}
@ -158,3 +179,14 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
func getNewTaskOpts(_ *cli.Context) []containerd.NewTaskOpts {
return nil
}
func getNetNSPath(ctx gocontext.Context, t containerd.Task) (string, error) {
s, err := t.Spec(ctx)
if err != nil {
return "", err
}
if s.Windows == nil || s.Windows.Network == nil {
return "", nil
}
return s.Windows.Network.NetworkNamespace, nil
}

View File

@ -20,22 +20,17 @@ import (
"fmt"
"os"
"github.com/containerd/containerd/pkg/seed"
"github.com/containerd/imgcrypt/cmd/ctr/app"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var pluginCmds = []cli.Command{}
func init() {
seed.WithTimeAndRand()
}
var pluginCmds = []*cli.Command{}
func main() {
app := app.New()
app.Commands = append(app.Commands, pluginCmds...)
if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "ctr: %s\n", err)
application := app.New()
application.Commands = append(application.Commands, pluginCmds...)
if err := application.Run(os.Args); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "ctr: %s\n", err)
os.Exit(1)
}
}

91
cmd/go.mod Normal file
View File

@ -0,0 +1,91 @@
module github.com/containerd/imgcrypt/cmd
go 1.23.0
toolchain go1.23.7
require (
github.com/Microsoft/go-winio v0.6.2
github.com/Microsoft/hcsshim v0.12.9
github.com/containerd/console v1.0.4
github.com/containerd/containerd/api v1.8.0
github.com/containerd/containerd/v2 v2.0.5
github.com/containerd/errdefs v1.0.0
github.com/containerd/go-cni v1.1.12
github.com/containerd/imgcrypt/v2 v2.0.0
github.com/containerd/log v0.1.0
github.com/containerd/platforms v1.0.0-rc.1
github.com/containerd/typeurl/v2 v2.2.3
github.com/containers/ocicrypt v1.2.1
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0
github.com/opencontainers/runtime-spec v1.2.0
github.com/sirupsen/logrus v1.9.3
github.com/urfave/cli/v2 v2.27.5
golang.org/x/sync v0.12.0
google.golang.org/grpc v1.68.1
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect
github.com/cilium/ebpf v0.11.0 // indirect
github.com/containerd/cgroups/v3 v3.0.3 // indirect
github.com/containerd/continuity v0.4.4 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/go-runc v1.1.0 // indirect
github.com/containerd/plugin v1.0.0 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containernetworking/cni v1.2.3 // indirect
github.com/containernetworking/plugins v1.5.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mdlayher/vsock v1.2.1 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/symlink v0.3.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/opencontainers/selinux v1.11.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/containerd/imgcrypt/v2 => ../

349
cmd/go.sum Normal file
View File

@ -0,0 +1,349 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
github.com/containerd/containerd/v2 v2.0.5 h1:2vg/TjUXnaohAxiHnthQg8K06L9I4gdYEMcOLiMc8BQ=
github.com/containerd/containerd/v2 v2.0.5/go.mod h1:Qqo0UN43i2fX1FLkrSTCg6zcHNfjN7gEnx3NPRZI+N0=
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/go-cni v1.1.12 h1:wm/5VD/i255hjM4uIZjBRiEQ7y98W9ACy/mHeLi4+94=
github.com/containerd/go-cni v1.1.12/go.mod h1:+jaqRBdtW5faJxj2Qwg1Of7GsV66xcvnCx4mSJtUlxU=
github.com/containerd/go-runc v1.1.0 h1:OX4f+/i2y5sUT7LhmcJH7GYrjjhHa1QI4e8yO0gGleA=
github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
github.com/containernetworking/cni v1.2.3 h1:hhOcjNVUQTnzdRJ6alC5XF+wd9mfGIUaj8FuJbEslXM=
github.com/containernetworking/cni v1.2.3/go.mod h1:DuLgF+aPd3DzcTQTtp/Nvl1Kim23oFKdm2okJzBQA5M=
github.com/containernetworking/plugins v1.5.1 h1:T5ji+LPYjjgW0QM+KyrigZbLsZ8jaX+E5J/EcKOE4gQ=
github.com/containernetworking/plugins v1.5.1/go.mod h1:MIQfgMayGuHYs0XdNudf31cLLAC+i242hNm6KuDGqCM=
github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM=
github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
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/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU=
github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA=
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

84
go.mod
View File

@ -1,26 +1,68 @@
module github.com/containerd/imgcrypt
module github.com/containerd/imgcrypt/v2
go 1.13
go 1.23.0
toolchain go1.23.7
require (
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3
github.com/Microsoft/hcsshim v0.8.14
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3 // indirect
github.com/containerd/console v1.0.1
github.com/containerd/containerd v1.5.0-beta.1
github.com/containerd/typeurl v1.0.1
github.com/containers/ocicrypt v1.1.0
github.com/gogo/protobuf v1.3.2
github.com/imdario/mergo v0.3.11 // indirect
github.com/containerd/containerd/v2 v2.0.4
github.com/containerd/errdefs v1.0.0
github.com/containerd/platforms v1.0.0-rc.1
github.com/containerd/typeurl/v2 v2.2.3
github.com/containers/ocicrypt v1.2.1
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d
github.com/pkg/errors v0.9.1
github.com/prometheus/procfs v0.6.0 // indirect
github.com/sirupsen/logrus v1.7.0
github.com/urfave/cli v1.22.2
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
google.golang.org/grpc v1.33.2
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools/v3 v3.0.3 // indirect
github.com/opencontainers/image-spec v1.1.0
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/containerd/cgroups/v3 v3.0.3 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
github.com/containerd/continuity v0.4.4 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/plugin v1.0.0 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/opencontainers/selinux v1.11.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/grpc v1.68.1 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

920
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -18,33 +18,34 @@ package encryption
import (
"context"
"fmt"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/core/diff"
"github.com/containerd/errdefs"
"github.com/containerd/typeurl/v2"
"github.com/containerd/imgcrypt/v2"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/diff"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/imgcrypt"
"github.com/containerd/typeurl"
encconfig "github.com/containers/ocicrypt/config"
"github.com/gogo/protobuf/types"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// WithDecryptedUnpack allows to pass parameters the 'layertool' needs to the applier
func WithDecryptedUnpack(data *imgcrypt.Payload) diff.ApplyOpt {
return func(_ context.Context, desc ocispec.Descriptor, c *diff.ApplyConfig) error {
if c.ProcessorPayloads == nil {
c.ProcessorPayloads = make(map[string]*types.Any)
}
data.Descriptor = desc
any, err := typeurl.MarshalAny(data)
anything, err := typeurl.MarshalAny(data)
if err != nil {
return errors.Wrapf(err, "failed to marshal payload")
return fmt.Errorf("failed to marshal payload: %w", err)
}
if c.ProcessorPayloads == nil {
c.ProcessorPayloads = make(map[string]typeurl.Any, len(imgcrypt.PayloadToolIDs))
}
for _, id := range imgcrypt.PayloadToolIDs {
c.ProcessorPayloads[id] = any
c.ProcessorPayloads[id] = anything
}
return nil
}

View File

@ -20,22 +20,22 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/containerd/v2/core/images/converter"
"github.com/containerd/errdefs"
"github.com/containerd/platforms"
"github.com/containers/ocicrypt"
encconfig "github.com/containers/ocicrypt/config"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/platforms"
encocispec "github.com/containers/ocicrypt/spec"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go"
"github.com/pkg/errors"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@ -50,10 +50,17 @@ const (
// LayerFilter allows to select Layers by certain criteria
type LayerFilter func(desc ocispec.Descriptor) bool
// isLocalPlatform determines whether the given platform matches the local one
func isLocalPlatform(platform *ocispec.Platform) bool {
matcher := platforms.NewMatcher(*platform)
return matcher.Match(platforms.DefaultSpec())
}
// IsEncryptedDiff returns true if mediaType is a known encrypted media type.
func IsEncryptedDiff(ctx context.Context, mediaType string) bool {
func IsEncryptedDiff(_ context.Context, mediaType string) bool {
switch mediaType {
case encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerEnc:
case encocispec.MediaTypeLayerZstdEnc, encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerEnc:
return true
}
return false
@ -106,17 +113,21 @@ func encryptLayer(cc *encconfig.CryptoConfig, dataReader content.ReaderAt, desc
newDesc.MediaType = encocispec.MediaTypeLayerEnc
case encocispec.MediaTypeLayerGzipEnc:
newDesc.MediaType = encocispec.MediaTypeLayerGzipEnc
case encocispec.MediaTypeLayerZstdEnc:
newDesc.MediaType = encocispec.MediaTypeLayerZstdEnc
case encocispec.MediaTypeLayerEnc:
newDesc.MediaType = encocispec.MediaTypeLayerEnc
// TODO: Mediatypes to be added in ocispec
case ocispec.MediaTypeImageLayerGzip:
newDesc.MediaType = encocispec.MediaTypeLayerGzipEnc
case ocispec.MediaTypeImageLayerZstd:
newDesc.MediaType = encocispec.MediaTypeLayerZstdEnc
case ocispec.MediaTypeImageLayer:
newDesc.MediaType = encocispec.MediaTypeLayerEnc
default:
return ocispec.Descriptor{}, nil, nil, errors.Errorf("Encryption: unsupporter layer MediaType: %s\n", desc.MediaType)
return ocispec.Descriptor{}, nil, nil, fmt.Errorf("unsupporter layer MediaType: %s", desc.MediaType)
}
return newDesc, encLayerReader, encLayerFinalizer, nil
@ -138,10 +149,12 @@ func DecryptLayer(dc *encconfig.DecryptConfig, dataReader io.Reader, desc ocispe
switch desc.MediaType {
case encocispec.MediaTypeLayerGzipEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2LayerGzip
case encocispec.MediaTypeLayerZstdEnc:
newDesc.MediaType = ocispec.MediaTypeImageLayerZstd
case encocispec.MediaTypeLayerEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2Layer
default:
return ocispec.Descriptor{}, nil, "", errors.Errorf("Decryption: unsupporter layer MediaType: %s\n", desc.MediaType)
return ocispec.Descriptor{}, nil, "", fmt.Errorf("unsupporter layer MediaType: %s", desc.MediaType)
}
return newDesc, resultReader, layerDigest, nil
}
@ -163,10 +176,12 @@ func decryptLayer(cc *encconfig.CryptoConfig, dataReader content.ReaderAt, desc
switch desc.MediaType {
case encocispec.MediaTypeLayerGzipEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2LayerGzip
case encocispec.MediaTypeLayerZstdEnc:
newDesc.MediaType = ocispec.MediaTypeImageLayerZstd
case encocispec.MediaTypeLayerEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2Layer
default:
return ocispec.Descriptor{}, nil, errors.Errorf("Decryption: unsupporter layer MediaType: %s\n", desc.MediaType)
return ocispec.Descriptor{}, nil, fmt.Errorf("unsupporter layer MediaType: %s", desc.MediaType)
}
return newDesc, resultReader, nil
}
@ -209,7 +224,7 @@ func cryptLayer(ctx context.Context, cs content.Store, desc ocispec.Descriptor,
if haveDigest {
if err := content.WriteBlob(ctx, cs, ref, resultReader, newDesc); err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config")
return ocispec.Descriptor{}, fmt.Errorf("failed to write config: %w", err)
}
} else {
newDesc.Digest, newDesc.Size, err = ingestReader(ctx, cs, ref, resultReader)
@ -223,7 +238,7 @@ func cryptLayer(ctx context.Context, cs content.Store, desc ocispec.Descriptor,
if encLayerFinalizer != nil {
annotations, err := encLayerFinalizer()
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "Error getting annotations from encLayer finalizer")
return ocispec.Descriptor{}, fmt.Errorf("error getting annotations from encLayer finalizer: %w", err)
}
for k, v := range annotations {
newDesc.Annotations[k] = v
@ -235,22 +250,22 @@ func cryptLayer(ctx context.Context, cs content.Store, desc ocispec.Descriptor,
func ingestReader(ctx context.Context, cs content.Ingester, ref string, r io.Reader) (digest.Digest, int64, error) {
cw, err := content.OpenWriter(ctx, cs, content.WithRef(ref))
if err != nil {
return "", 0, errors.Wrap(err, "failed to open writer")
return "", 0, fmt.Errorf("failed to open writer: %w", err)
}
defer cw.Close()
if _, err := content.CopyReader(cw, r); err != nil {
return "", 0, errors.Wrap(err, "copy failed")
return "", 0, fmt.Errorf("copy failed: %w", err)
}
st, err := cw.Status()
if err != nil {
return "", 0, errors.Wrap(err, "failed to get state")
return "", 0, fmt.Errorf("failed to get state: %w", err)
}
if err := cw.Commit(ctx, st.Offset, ""); err != nil {
if !errdefs.IsAlreadyExists(err) {
return "", 0, errors.Wrapf(err, "failed commit on ref %q", ref)
return "", 0, fmt.Errorf("failed commit on ref %q: %w", ref, err)
}
}
@ -258,7 +273,7 @@ func ingestReader(ctx context.Context, cs content.Ingester, ref string, r io.Rea
}
// Encrypt or decrypt all the Children of a given descriptor
func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp, thisPlatform *ocispec.Platform) (ocispec.Descriptor, bool, error) {
func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp, _ *ocispec.Platform) (ocispec.Descriptor, bool, error) {
children, err := images.Children(ctx, cs, desc)
if err != nil {
if errdefs.IsNotFound(err) {
@ -277,7 +292,8 @@ func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descripto
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
config = child
case images.MediaTypeDockerSchema2LayerGzip, images.MediaTypeDockerSchema2Layer,
ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayer:
ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayer,
ocispec.MediaTypeImageLayerZstd:
if cryptoOp == cryptoOpEncrypt && lf(child) {
nl, err := cryptLayer(ctx, cs, child, cc, cryptoOp)
if err != nil {
@ -288,7 +304,7 @@ func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descripto
} else {
newLayers = append(newLayers, child)
}
case encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerEnc:
case encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerZstdEnc, encocispec.MediaTypeLayerEnc:
// this one can be decrypted but also its recipients list changed
if lf(child) {
nl, err := cryptLayer(ctx, cs, child, cc, cryptoOp)
@ -300,11 +316,11 @@ func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descripto
} else {
newLayers = append(newLayers, child)
}
case images.MediaTypeDockerSchema2LayerForeign, images.MediaTypeDockerSchema2LayerForeignGzip:
case images.MediaTypeDockerSchema2LayerForeign, images.MediaTypeDockerSchema2LayerForeignGzip, "application/vnd.in-toto+json":
// never encrypt/decrypt
newLayers = append(newLayers, child)
default:
return ocispec.Descriptor{}, false, errors.Errorf("bad/unhandled MediaType %s in encryptChildren\n", child.MediaType)
return ocispec.Descriptor{}, false, fmt.Errorf("bad/unhandled MediaType %s in encryptChildren", child.MediaType)
}
}
@ -319,7 +335,7 @@ func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descripto
mb, err := json.MarshalIndent(newManifest, "", " ")
if err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to marshal image")
return ocispec.Descriptor{}, false, fmt.Errorf("failed to marshal image: %w", err)
}
newDesc := ocispec.Descriptor{
@ -338,7 +354,7 @@ func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descripto
ref := fmt.Sprintf("manifest-%s", newDesc.Digest.String())
if err := content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write config")
return ocispec.Descriptor{}, false, fmt.Errorf("failed to write config: %w", err)
}
return newDesc, true, nil
}
@ -380,6 +396,9 @@ func cryptManifestList(ctx context.Context, cs content.Store, desc ocispec.Descr
var newManifests []ocispec.Descriptor
modified := false
for _, manifest := range index.Manifests {
if cryptoOp == cryptoOpUnwrapOnly && !isLocalPlatform(manifest.Platform) {
continue
}
newManifest, m, err := cryptChildren(ctx, cs, manifest, cc, lf, cryptoOp, manifest.Platform)
if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, err
@ -389,6 +408,9 @@ func cryptManifestList(ctx context.Context, cs content.Store, desc ocispec.Descr
}
newManifests = append(newManifests, newManifest)
}
if cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, fmt.Errorf("No manifest found for local platform")
}
if modified {
// we need to update the index
@ -399,7 +421,7 @@ func cryptManifestList(ctx context.Context, cs content.Store, desc ocispec.Descr
mb, err := json.MarshalIndent(newIndex, "", " ")
if err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to marshal index")
return ocispec.Descriptor{}, false, fmt.Errorf("failed to marshal index: %w", err)
}
newDesc := ocispec.Descriptor{
@ -416,7 +438,7 @@ func cryptManifestList(ctx context.Context, cs content.Store, desc ocispec.Descr
ref := fmt.Sprintf("index-%s", newDesc.Digest.String())
if err = content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write index")
return ocispec.Descriptor{}, false, fmt.Errorf("failed to write index: %w", err)
}
return newDesc, true, nil
}
@ -428,7 +450,7 @@ func cryptManifestList(ctx context.Context, cs content.Store, desc ocispec.Descr
// representing a manifest list or a single manifest
func cryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) {
if cc == nil {
return ocispec.Descriptor{}, false, errors.Wrapf(errdefs.ErrInvalidArgument, "CryptoConfig must not be nil")
return ocispec.Descriptor{}, false, errors.New("invalid argument: CryptoConfig must not be nil")
}
switch desc.MediaType {
case ocispec.MediaTypeImageIndex, images.MediaTypeDockerSchema2ManifestList:
@ -436,7 +458,7 @@ func cryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor,
case ocispec.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest:
return cryptManifest(ctx, cs, desc, cc, lf, cryptoOp)
default:
return ocispec.Descriptor{}, false, errors.Errorf("CryptImage: Unhandled media type: %s", desc.MediaType)
return ocispec.Descriptor{}, false, fmt.Errorf("unhandled media type: %s", desc.MediaType)
}
}
@ -450,19 +472,41 @@ func DecryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor
return cryptImage(ctx, cs, desc, cc, lf, cryptoOpDecrypt)
}
// GetImageEncryptConverter returns a converter function for image encryption
func GetImageEncryptConverter(cc *encconfig.CryptoConfig, lf LayerFilter) converter.ConvertFunc {
return func(ctx context.Context, cs content.Store, desc ocispec.Descriptor) (*ocispec.Descriptor, error) {
newDesc, _, err := EncryptImage(ctx, cs, desc, cc, lf)
if err != nil {
return nil, err
}
return &newDesc, nil
}
}
// GetImageDecryptConverter returns a converter function for image decryption
func GetImageDecryptConverter(cc *encconfig.CryptoConfig, lf LayerFilter) converter.ConvertFunc {
return func(ctx context.Context, cs content.Store, desc ocispec.Descriptor) (*ocispec.Descriptor, error) {
newDesc, _, err := DecryptImage(ctx, cs, desc, cc, lf)
if err != nil {
return nil, err
}
return &newDesc, nil
}
}
// CheckAuthorization checks whether a user has the right keys to be allowed to access an image (every layer)
// It takes decrypting of the layers only as far as decrypting the asymmetrically encrypted data
// The decryption is only done for the current platform
func CheckAuthorization(ctx context.Context, cs content.Store, desc ocispec.Descriptor, dc *encconfig.DecryptConfig) error {
cc := encconfig.InitDecryption(dc.Parameters)
lf := func(desc ocispec.Descriptor) bool {
lf := func(_ ocispec.Descriptor) bool {
return true
}
_, _, err := cryptImage(ctx, cs, desc, &cc, lf, cryptoOpUnwrapOnly)
if err != nil {
return errors.Wrapf(err, "you are not authorized to use this image")
return fmt.Errorf("you are not authorized to use this image: %w", err)
}
return nil
}

View File

@ -14,11 +14,13 @@
limitations under the License.
*/
package images
// Package parsehelpers provides parse helpers for CLI applications.
// This package does not depend on any specific CLI library such as github.com/urfave/cli .
package parsehelpers
import (
"errors"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
@ -28,12 +30,17 @@ import (
"github.com/containers/ocicrypt/config/pkcs11config"
"github.com/containers/ocicrypt/crypto/pkcs11"
encutils "github.com/containers/ocicrypt/utils"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
type EncArgs struct {
GPGHomedir string // --gpg-homedir
GPGVersion string // --gpg-version
Key []string // --key
Recipient []string // --recipient
DecRecipient []string // --dec-recipient
}
// processRecipientKeys sorts the array of recipients by type. Recipients may be either
// x509 certificates, public keys, or PGP public keys identified by email address or name
func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, [][]byte, [][]byte, [][]byte, error) {
@ -50,7 +57,7 @@ func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, []
idx := strings.Index(recipient, ":")
if idx < 0 {
return nil, nil, nil, nil, nil, nil, errors.New("Invalid recipient format")
return nil, nil, nil, nil, nil, nil, errors.New("invalid recipient format")
}
protocol := recipient[:idx]
@ -61,43 +68,43 @@ func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, []
gpgRecipients = append(gpgRecipients, []byte(value))
case "jwe":
tmp, err := ioutil.ReadFile(value)
tmp, err := os.ReadFile(value)
if err != nil {
return nil, nil, nil, nil, nil, nil, errors.Wrap(err, "Unable to read file")
return nil, nil, nil, nil, nil, nil, fmt.Errorf("unable to read file: %w", err)
}
if !encutils.IsPublicKey(tmp) {
return nil, nil, nil, nil, nil, nil, errors.New("File provided is not a public key")
return nil, nil, nil, nil, nil, nil, errors.New("file provided is not a public key")
}
pubkeys = append(pubkeys, tmp)
case "pkcs7":
tmp, err := ioutil.ReadFile(value)
tmp, err := os.ReadFile(value)
if err != nil {
return nil, nil, nil, nil, nil, nil, errors.Wrap(err, "Unable to read file")
return nil, nil, nil, nil, nil, nil, fmt.Errorf("unable to read file %s: %w", value, err)
}
if !encutils.IsCertificate(tmp) {
return nil, nil, nil, nil, nil, nil, errors.New("File provided is not an x509 cert")
return nil, nil, nil, nil, nil, nil, errors.New("file provided is not an x509 cert")
}
x509s = append(x509s, tmp)
case "pkcs11":
tmp, err := ioutil.ReadFile(value)
tmp, err := os.ReadFile(value)
if err != nil {
return nil, nil, nil, nil, nil, nil, errors.Wrap(err, "Unable to read file")
return nil, nil, nil, nil, nil, nil, fmt.Errorf("unable to read file %s: %w", value, err)
}
if encutils.IsPkcs11PublicKey(tmp) {
pkcs11Yamls = append(pkcs11Yamls, tmp)
} else if encutils.IsPublicKey(tmp) {
pkcs11Pubkeys = append(pkcs11Pubkeys, tmp)
} else {
return nil, nil, nil, nil, nil, nil, errors.New("Provided file is not a public key")
return nil, nil, nil, nil, nil, nil, errors.New("provided file is not a public key")
}
case "provider":
keyProvider = append(keyProvider, []byte(value))
default:
return nil, nil, nil, nil, nil, nil, errors.New("Provided protocol not recognized")
return nil, nil, nil, nil, nil, nil, errors.New("provided protocol not recognized")
}
}
return gpgRecipients, pubkeys, x509s, pkcs11Pubkeys, pkcs11Yamls, keyProvider, nil
@ -110,14 +117,14 @@ func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, []
// - <password>
func processPwdString(pwdString string) ([]byte, error) {
if strings.HasPrefix(pwdString, "file=") {
return ioutil.ReadFile(pwdString[5:])
return os.ReadFile(pwdString[5:])
} else if strings.HasPrefix(pwdString, "pass=") {
return []byte(pwdString[5:]), nil
} else if strings.HasPrefix(pwdString, "fd=") {
fdStr := pwdString[3:]
fd, err := strconv.Atoi(fdStr)
if err != nil {
return nil, errors.Wrapf(err, "could not parse file descriptor %s", fdStr)
return nil, fmt.Errorf("could not parse file descriptor %s: %w", fdStr, err)
}
f := os.NewFile(uintptr(fd), "pwdfile")
if f == nil {
@ -127,7 +134,7 @@ func processPwdString(pwdString string) ([]byte, error) {
pwd := make([]byte, 64)
n, err := f.Read(pwd)
if err != nil {
return nil, errors.Wrapf(err, "could not read from file descriptor")
return nil, fmt.Errorf("could not read from file descriptor: %w", err)
}
return pwd[:n], nil
}
@ -171,7 +178,7 @@ func processPrivateKeyFiles(keyFilesAndPwds []string) ([][]byte, [][]byte, [][]b
}
keyfile := parts[0]
tmp, err := ioutil.ReadFile(keyfile)
tmp, err := os.ReadFile(keyfile)
if err != nil {
return nil, nil, nil, nil, nil, nil, err
}
@ -195,12 +202,12 @@ func processPrivateKeyFiles(keyFilesAndPwds []string) ([][]byte, [][]byte, [][]b
return gpgSecretKeyRingFiles, gpgSecretKeyPasswords, privkeys, privkeysPasswords, pkcs11Yamls, keyProviders, nil
}
func createGPGClient(context *cli.Context) (ocicrypt.GPGClient, error) {
return ocicrypt.NewGPGClient(context.String("gpg-version"), context.String("gpg-homedir"))
func CreateGPGClient(args EncArgs) (ocicrypt.GPGClient, error) {
return ocicrypt.NewGPGClient(args.GPGVersion, args.GPGHomedir)
}
func getGPGPrivateKeys(context *cli.Context, gpgSecretKeyRingFiles [][]byte, descs []ocispec.Descriptor, mustFindKey bool) (gpgPrivKeys [][]byte, gpgPrivKeysPwds [][]byte, err error) {
gpgClient, err := createGPGClient(context)
func getGPGPrivateKeys(args EncArgs, gpgSecretKeyRingFiles [][]byte, descs []ocispec.Descriptor, mustFindKey bool) (gpgPrivKeys [][]byte, gpgPrivKeysPwds [][]byte, err error) {
gpgClient, err := CreateGPGClient(args)
if err != nil {
return nil, nil, err
}
@ -219,26 +226,26 @@ func getGPGPrivateKeys(context *cli.Context, gpgSecretKeyRingFiles [][]byte, des
// CreateDecryptCryptoConfig creates the CryptoConfig object that contains the necessary
// information to perform decryption from command line options and possibly
// LayerInfos describing the image and helping us to query for the PGP decryption keys
func CreateDecryptCryptoConfig(context *cli.Context, descs []ocispec.Descriptor) (encconfig.CryptoConfig, error) {
func CreateDecryptCryptoConfig(args EncArgs, descs []ocispec.Descriptor) (encconfig.CryptoConfig, error) {
ccs := []encconfig.CryptoConfig{}
// x509 cert is needed for PKCS7 decryption
_, _, x509s, _, _, _, err := processRecipientKeys(context.StringSlice("dec-recipient"))
_, _, x509s, _, _, _, err := processRecipientKeys(args.DecRecipient)
if err != nil {
return encconfig.CryptoConfig{}, err
}
gpgSecretKeyRingFiles, gpgSecretKeyPasswords, privKeys, privKeysPasswords, pkcs11Yamls, keyProviders, err := processPrivateKeyFiles(context.StringSlice("key"))
gpgSecretKeyRingFiles, gpgSecretKeyPasswords, privKeys, privKeysPasswords, pkcs11Yamls, keyProviders, err := processPrivateKeyFiles(args.Key)
if err != nil {
return encconfig.CryptoConfig{}, err
}
_, err = createGPGClient(context)
_, err = CreateGPGClient(args)
gpgInstalled := err == nil
if gpgInstalled {
if len(gpgSecretKeyRingFiles) == 0 && len(privKeys) == 0 && len(pkcs11Yamls) == 0 && len(keyProviders) == 0 && descs != nil {
// Get pgp private keys from keyring only if no private key was passed
gpgPrivKeys, gpgPrivKeyPasswords, err := getGPGPrivateKeys(context, gpgSecretKeyRingFiles, descs, true)
gpgPrivKeys, gpgPrivKeyPasswords, err := getGPGPrivateKeys(args, gpgSecretKeyRingFiles, descs, true)
if err != nil {
return encconfig.CryptoConfig{}, err
}
@ -295,14 +302,14 @@ func CreateDecryptCryptoConfig(context *cli.Context, descs []ocispec.Descriptor)
}
// CreateCryptoConfig from the list of recipient strings and list of key paths of private keys
func CreateCryptoConfig(context *cli.Context, descs []ocispec.Descriptor) (encconfig.CryptoConfig, error) {
recipients := context.StringSlice("recipient")
keys := context.StringSlice("key")
func CreateCryptoConfig(args EncArgs, descs []ocispec.Descriptor) (encconfig.CryptoConfig, error) {
recipients := args.Recipient
keys := args.Key
var decryptCc *encconfig.CryptoConfig
ccs := []encconfig.CryptoConfig{}
if len(keys) > 0 {
dcc, err := CreateDecryptCryptoConfig(context, descs)
dcc, err := CreateDecryptCryptoConfig(args, descs)
if err != nil {
return encconfig.CryptoConfig{}, err
}
@ -317,7 +324,7 @@ func CreateCryptoConfig(context *cli.Context, descs []ocispec.Descriptor) (encco
}
encryptCcs := []encconfig.CryptoConfig{}
gpgClient, err := createGPGClient(context)
gpgClient, err := CreateGPGClient(args)
gpgInstalled := err == nil
if len(gpgRecipients) > 0 && gpgInstalled {
gpgPubRingFile, err := gpgClient.ReadGPGPubRingFile()
@ -376,7 +383,6 @@ func CreateCryptoConfig(context *cli.Context, descs []ocispec.Descriptor) (encco
if len(ccs) > 0 {
return encconfig.CombineCryptoConfigs(ccs), nil
} else {
return encconfig.CryptoConfig{}, nil
}
return encconfig.CryptoConfig{}, nil
}

View File

@ -17,7 +17,8 @@
package imgcrypt
import (
"github.com/containerd/typeurl"
"github.com/containerd/typeurl/v2"
encconfig "github.com/containers/ocicrypt/config"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

View File

@ -40,20 +40,37 @@ NGINX=docker.io/library/nginx:latest
NGINX_ENC=docker.io/library/nginx:enc
NGINX_DEC=docker.io/library/nginx:dec
BASH=docker.io/library/bash:latest
BASH_ENC=docker.io/library/bash:enc
# gpg2 --export-secret-key ...
GPGTESTKEY1="lQOYBF9qNH4BCADnPy49qS3b36Sf0CjBL98lvNqOMotHupF0JUvNYcQq39OmOcRVUu1DVtWw7YDcVToO2gM+xSEQ677xxu+k0VcpfyGYQRoQSTxkvXlH9Qb9nZouizy0DstWwgquePRiK7sLKPbiZOcIXcYBKUwR6oQM2aYTuzaXax5wyqejczwOPqZ7Ww5aA9r2a1xEepSEjxPJ7+zNw3k2nWmL2uvX/gx7yCn78N3jQhLx8AMIE7eLk0QMTi1LldFWGz2V3z1SBOkdn2eUTsrQs2tBrq1oMEVHYwZqM6n+PW2Sqhycrj6sVoK2vyfrC4E/bz7Spn4qIF3Q/ZShpHEI5lSELAYTcJtZABEBAAEAB/wMWcFCPVIr82CjQXafxMsGBLVmkVgDg3knyyMmi8FyqcQv1VeBWB3AcjeVDMZMXkfsyaORO22V7gVje+TKOH0PhBD7BQUbmBG/7qe22mUecAevUzPxiPW+wzvXUDH7OUsy4CP5ePqm5X1BDB/aOByH5Cr81Euo4Cl+zDASaIHtX7y33WwB8/ybJpcp14tF75Wb0CEzFGeNX+VqwWmppexuvvRkzPiTNOAk2k9domb3JHbrfifs0HVkijUEW3Ke7yuOmci0wnhoHfJOzMPWtJYYEj//xsoQl5TN3rl5oLj5WZN4uIoYKBj8nZmbfkWdN1WF4xYSANisd6R0z2CWB2chBADonAYjJ2uIC516DOClxMlnH547olOw1YUNL8VGC7Wau0OYXnnZK/YiOYAxQlfP0ZNu8je5K7QkI8qoy9HDSRKxzO/2w0kA0M1B8ZvsgFMRgx+fYjrlETSbuwuel5x93i7M3o7BYUipNImSzGG+i18AQAP/n0Bgb/IQ2bzt9nxVEQQA/oAT9qvNgkg0dFnjPqKZxTXihZ6C1/31+6khFNWxch+kPtdYT0j9jDZwIe9wfjQ3qx/AFYXvcqZlKlCAHx1+rXL/nRkfmC+955SibBrUBRM1X4hYAYwievVURNZD3P6a6kfekrLsbjJil9A6ibf0fU9mKHLBMVJod6IufBSwwckD/09P3lerDMi7LyFK1AAWYuNPATfbKjU+Dg57r1xCpbPNiU2OXeI0m4bCaPBh8Ga1rg21/CXNENs2SyU4Gsp5TBkzq0bYqXuf2OWOBh3W5LDt/EKGvzn1q3i+Y6+JEBhZKhgHxbmMIU0IjoHs09TMDQXk7Ro1GuYsemydcKko00sNP0S0G3Rlc3RrZXkxIDx0ZXN0a2V5MUBrZXkub3JnPokBVAQTAQgAPgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEH3fP8yEk2a+7XT5wexL41UPN6LBQJfajSdBQkSzAMfAAoJEAexL41UPN6LEvAIANRR49Pq66v674qg6J6v5o5Q9VLSkAJSqRiajkfQks2L164LQglW98ijvTA1s5X4YA+zspllwm29uOwl9PGZmmxC5Oj47W+djHmluM5IToEezCC6sMr/ay0C5zbb2+H4pgZvqDv6/GZKCmXzdJnVag8T8kT0gmL7DnpivbWHVTjr4dVFWedLzmmG3lWVJpmXDsJ/UOunA9jnmvVCwkvAa0JG7KOwtG/pNOyki7MHPk6NbDaT9XL15kX9e7ZcTHHy8Z3HG0e5y6HC6puOicKxwS4ywbm82OXKcgcTCy6IRC9baroEKRwAs5hero3ziTJ1eO/zye38i5yKaWyuh37IDVydA5gEX2o0fgEIAO3fcJRGYKVNCziQAAkqYDdU6zkt96aFI+LDr3RwIYcdfoP4IXW0IqSQIv2YdzWLUbJNTszSxzbwVdpOLTRYctnKu/AUkzJIDppXXVGEGmeQgu21VjwZqEvcxsrAOpZQ3Sg1yzy2q4U4T3kKpB7BQ7ZqOzBdFfQCC+PDpXhcxx2ubYbUcAov5bJLQwu0jteWHizIVin6mnpW7lv7pSkm0YczsZ41ODlGj+fWiEUI542zZRzgHDSu8kCVJWQtM/rSVEnZgFEAuZQxe9F936ZlyzqPYibyIiU7PDy3vR4i3S2kWp2CExXvSaGqIRcWj5qb5PJDnozfh0KuqnSrJxi+5GUAEQEAAQAH/2J/D/3FuoUYBtpv/iPNcTPYLOJrX02LedWPE9rSB4AMPXPlze0QHvwnVuXNOSdpvfVnv4ZejPD5yYLwthUjvsLiCLobuuuqHKnaHSEA43IYy64kVUXjleV70LDpshjF+R2KUNKeDR3HuFi1iEnX2vLwv/uBv/Je2o+AVsclG6n04LUeXlJTjOdv31g707He6aftp1OHLi0MVcomZXPbbqMOVzIPjpweGjI9HS32rg/z1C09c11zTKz2asdFdkl3fkfdsrwYdVp31EDy8JHbYmI3MflmIg6zgyOP5jtdjwWIMwXI9QK14Go+ZdHakA/d3QRcadQXwbWDkU16drWYEO0EAPUu6Ig+5grP1mAKyqxs+3zDTivk4IGnM76e0KSVnK3Ixh7JmpFdH9mIQQ5EF3pYsL8xspfN8gMlKqHEyll9YwbRRniUiaurP3WCgpTEfX+uS+1czk7trTIZKEVtM7wN9FS4R7WJEdvS3FnZ06B9G391duUR+QQmlWFjtCtladqDBAD4Xfe9kAvuO6m3j47EDW6SEKW6JlMgPzHWokVfeeGaeuvZfAZZW0DGRnWRcgtD2BmF/qIoeyo7Df280lDi+LJLCKKgtEZc+7KdUIIOZWJV5BNXzc8AzULMcT7xs/lEUdp86Pkr8SefhZdfwAuk+PAEHJKha2oTQLoVRnl1L6Gw9wQAh/WNnBAIm/Zhn4BuABeaHt0/UXF6cKF25rJlOdF1kagyytYyEKK+cUjczdqdw7mxm7D4RmWZP+JnGH8w3hOssptDz5iV1K6pArSzg0dkZ45sQ9SNA1Ckxq0R54gSokoGpM4vGgY7QnKLOJE+R2W7Ko+XnH8NP8OjvviEyZbicJVHUIkBPAQYAQgAJgIbDBYhBEH3fP8yEk2a+7XT5wexL41UPN6LBQJfajSsBQkSzAMuAAoJEAexL41UPN6L/PAIAIsfpMF1c34C2S2FcdTjEXtatj3CQCqt+n6PKWxh9+siLQE8cTpvGl3chRsKtVF6BZqX8oqMkK9pEKlVfXgxcgtYjF908YHEWyor3D/5WE0xVRFlXfqQZGwoeDgsjCq6tQ7evbGmWwc46eeCbAk0Um3idHF7IyNJ7ubN8r3rFL66r5+uk8rOPBBwXL6Qiez7oUXbXEz3MA0pgtnjp3UoGSLzMP8zZYsbq6IgodADjTiVD/1iUo1qT53PbXwIxMDkJhb2v+qz7tQPiU+6nbx6WtP2iaiPcZZF4LlnfV9On5hkutLSfk9Fum+4c05XvzI5BR+gKfjDno6HFDVQwxyLFgs="
GPGTESTKEY2="lQOYBF9qNRkBCADFaEsRhp43RurrJJVmQKxDhDJxPsLZH04SWjLPvALd47yBAJjKSUNJywrS1Px1bb5FneeSnBriUhmKjiVhL2hKfWjHdfs7nCK4MiuNwUtZ/tlKniVTrBBp7DqTfIxCHVAQ3nf0NALZU9054McSMALHG3FfEabz3UcloodgBYWyqFEJw48V4/WIHAkgclfARW5YPtseOfKyKgf5VQ1M4X3EfwjD5jRHXxSr241PXYs7KQFEYbuNbzEHc9P7yg2hURx5Dl3xMCjPlyndI8/AsTqo0MTxkDYcTkaNWqL9BsUyjKEox7Cg625hJVWiz+CGXNXri6ZXvETifNFIIiyNhImdABEBAAEAB/9FzhvhfidbZ53xeXXE+zCPDWOi7O0Mxwed8LxP/e1LlljViyb8PQzovr48kGkXgy+JwY0eKEpPZnW2q44nQBLSaGdRRPSKfys91CvXjBb/o2EmBCcx38HMGucZuSyFwoTJ+kkTlwK84+1yJnxuf4Cz9I3R7tWJHWGnusHBICLHaiKkLdFLzweD5IFz5ElTlPbGgFicWrkykllHWee/tOb7DUtj2u5NO7LZ9t8TJnD6hwRGgA8961d4U5j6FtW7pfSf7OeQ4s1X6JZE4q7Z/chu9cptoCgQ8SLjuRrgpiHQj4sXspjMwZOzNjFmeipBG/AvJsZ+gvQCG2XUX9hOR2VXBADKYFR8EXApKKLuZbD+khTKCvVi2GdGw3ceR7YvZc1tw7U/uSFbirwqvPQC79IzJogurpcJUBO4EpP0Vb6zgyPARmAO0Ky9+BEQ4qQYSv+k/0yseyb9GcMh3Nt0FMNt10XUJTTKemQqy1oFS3Zlm8rtJrD/3KPE7CDZL57WlegYxwQA+bbpRbboLpbfJM+JbvdAfssg+L4mbpv+Bqq7IOTHbKsOz5A942aLZQS4FXMyMkYbKR5hLRyMREMlBatFf3YP4n475M45FQNHv5spWfyfBvFPoX8xMS1CMuQ0xDuVBTedNhvf0n031tma5phzvEPc/AFzaC4j1V5gFERk0UjsTnsD/R0JUBWOMhNlO9ubno+MJJ8qi6catOVuaPWI4wdUx8+5b3iF/O5TuBue/+KOxRQfsYQUVYwvEPTmIjlcDnyPZZUX/0S/goILb+0uQWFx10+Cgc8Glz2hhUq1Kwd4loerCK2UrkR7EEdO6ggvVKilgPI0GPQ/D72SMwPXM0kFjEDFPjy0G3Rlc3RrZXkyIDx0ZXN0a2V5MkBrZXkub3JnPokBVAQTAQgAPgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBIANymm4rDLWbISinnngqkpJ74AxBQJfajUsBQkSzAMTAAoJEHngqkpJ74AxK4EIAKi+IkImkOvJFNvnxUoXnmqgAm4jv2VhCMrGTwJqbxJuUYBakynEijUmqJT5OrX6BVmVKBzij5NNzRONpLLvocCxUD1xkaS7fEm8vi3lvuboarBjkYxQuRSGqRpc6Ij47o88oSQTmFHjFspkS7UwFdtpVNy2vN7F65NTtqd/5lRtvXjHSJ3+loWWAyoL0VGvCBAvkmzjcT+tPqeD+BF/gBpeslMrHu1mlMON+6j51nZC5qrjSODYjzfciZHZlaPQNWjLUhNQ+k8s2cFGgwzM6z4gm4KI5hm/Bul91A/MDRKHUwuqW3AA9eYcQe7G2L4OmL+UiY1zWCWglMjZZP3BZq2dA5gEX2o1GQEIANiHIhKwKTD+PAZN+oftZd+XwifHlWY4XMxKJuh6LNqQzCeYYwCGrfLDVW/xcaDaMdoaSCGWPwsvWAzyfCQ7pfPem4l/KLSejchgxYiHyeCZ/BGYxXe50xV6hacvI6MIfdOi3/H03NW9iVILqCesoB2YW0qTIgQRYnqNJxrQLMn6Ex8/xZnvNWApk1JJmeldptOUBvnjK0YC2IJlQzomiIcVBhYj9XD3ExcMS8Df9mWmFgVHAO+LD4/r73bhVMorU+lbtrftdbRQ5Sg9/43APXxzjcNaJ7VzOBqt624ZJomEOnqs+pwetMzysFuPauAU40dHI3Nz3IKF0B4WyYuTgl0AEQEAAQAH/juTBplstZCgyoQTjWQ7uYVI3GcUfzMGO+YLWuQoxVGHeFxGjaq144NBIi8wF4rhrcir5X+0NnlN1+SMDQLtFG5iJ5ovjdQQMcNZeM/lSHKO+28eAOq9imnE8aP7kMsJCZGipQoNzHrUcMVNpsDvuogaBLgifj/vRpCgaIt0jnYtYejYKX+/LvaQu+KQkGXa0VCyWQk/IT0ExOTCgfFaWp1BNzH3GwhnKtXp7gafcM2fBK8AExrBs9VeBWSRopRO2Koyq4hi+9NSY1nY8pTixlYKzttIOLGjT+xhR/+gXmVzJGC4WueZWLWeLPER9pKap0rxRpFAM80z0UR8C1MNksMEAOluyEwAY7OsnjyeGHcQc4YxM/u+AwMrcO/Wdm80k6ASb7GNiqc2FKMwhoXlEk+ee6i0F1MK6B7bLqOYQ4IsErIppEAAg96Td2ec8uWbSpGMcB17HJ6T2CKZHaEGSWVZAzCXVSt7fWHpXmqp2EouMHgnlWUvJiex7txII2c4a/IzBADtdfj1EY2DOVoNnl8NDGQk+KvuwnxVyfFlAwczxVc5BGHqKDyq8FL7wfe2/HGN3Ff5mBPCNkKLv1qilf1KdyzXmVJ7S1d/99K+g9tXMmu+mHL13wIgiLp1l1qMVuWyRUY22JP9cKD6igm4HU3uIxeCWW8fNMSNQyO2ej0DrwjJLwQAo73wTAfPBxe7PxHS8HYslQ2Y2YJuylGTY7n9pOCLlrfFXWVk0DW1pk/LlMXUcAp6i+BS9Y7wvv+VFvUmaz1yi56qwUW/Oeki2Mhiz7IA5VVkSPqg02N0upvb7efdK49YqC2/Ew/YExfaCWDc2fu6ZwX34mNHcFiw/HjRGwH3RbM+FokBPAQYAQgAJgIbDBYhBIANymm4rDLWbISinnngqkpJ74AxBQJfajU6BQkSzAMhAAoJEHngqkpJ74Axm+EH/jFB3OV8LxjHgTVOVR7OnxVJ+tIONFS8fcl+0ScDsDxrdyZZMYPFRF0WftgFtx4FpEx59Wz1IXqpuiJsnWGfq1dzwZCKZDx9awuiinn4n/1ifH/zXzEeiSGG5XWdfExsjumUCM9e6gNIw0PIFxvVpHHhqnAaUrVWaY+8UjWH/Mw0DZ/J09UubLv7r1LMzsjvzwI1VqOFa+Pw9WLEid0oDKpkLAbwyhprByW08VjI3phk2xLaxdeqIQq7b8ptUC4JE00VEzTDCj7MZLy2jqn4z4EOtuHE6+xYlCpoXCFY9fEiy7lJrI0I4ldGePoJcbn1WkSJOeR0Cb4dK43pFFEi5gk="
JWKTESTKEY1='{"d":"pGBXnqbPbMR6PIPkyzz1OhmJq4ORmlHwh2GunXJuzSj1AhYL9rZ8fd_NNn128yPmllTN6LOBqNj1vqXOnMaeu63vdn08z8xTDlCsuUt2T0NzgQlPuducu8K0OURFqf-C3dIPqipxnWKydN7_gYEEYosxgKU3B8WolA65YFTaUxv-NQL-3rASUTtiQ1rtm2l-RBEIqOuFh350Bahnq_gtINxKpVahpLDiLTte6HpnbzU7ei_dW4v3j6foMg2pOWUAcfxNfmZwQO-eEge88E5WfN7HIQnBTTjAjrNwIP-SfaDmKpa37at1kTG932If0VopQ9CJZE_jM2wHx3VfZiTmAQ","dp":"RFUZdzAaCs3ak4lxptnHy5J_ujWgHk1CvzyIU1tEw1P9BCme-pW30YdEvXkXMzqiX8g0p6WdEvbfx0I9dctje9IbjCQcemxjIUx-2ifUppp8_I4BCaZ4K4puyt65TJL2za6PmyuVTDlugYceMIupmZ4bx6C70bjTeo1ErVe-yYE","dq":"zOCBbLcqCtkXUQqlmOEmb35GBc5HLV6LcQSYAm1mhMIRjK-cSiXAlg4yKhXoGNAuU-LBXyVLeOa4cNdG_v-34XZGmqIyBWG1ehmMumcblzI2-Cuj76jW26sWBvPBH7cyEf1FULS3acF-xPd8TkNA9P0laZmCshOfa_-zkMM5Tf8","e":"AQAB","kty":"RSA","n":"zMwIcZn24y3Aj-P5Vox-w54FkpoRGeYGhyF7rdDQN2bYO-8h09doVbbgstYauyZKRvk3iWoOwfY9foD0hHCJNtT20sqbx40osGN9qLERweO6Xn8adhVPN7isTT9KozdvsrOIBr7uQUsruvow4klIYrv5FqS_RHpy4f0CUlsjPqc3F5PC4yV0D0f_QUApr06--uHRdH3ucunvdwR1V1IZV0DEJwZ5DzEDQmynzo5oV1UVNb9DSzTXsUAzSipCrdIyUxCnofPp_PzKvqMbctBAchx0AKN8IK8Z3RGFYyrV3HxkXqFxZ4aTVnkXqlnGV5CRQhx59ckIWUxAlyLcGLXvmw","p":"_aCnqjENQBE-he_7XWBo7kXJHnOz6SucuLNPo35imTO4nJBkga9HOF8VxeM3OrskEFVudkDvSqbq4KtERiCGL8f3-LAUKSFaxULa0h9FPJOlks_JXVlDwGsXOyHirIHIEvvbjAAQlV_F7tQNCzSHuXmegh3yJWLwz6EcUw2z9YE","q":"zrZyXsm2jVHc9JkWEp8CMJ0J65f87KrYjQgcb46XkCK1E7bnFDLiNzYV-CQ8a9kKuWfd_LUx2FIjwrik5IFQXJA7Z7s4jvAh2J-pLutSD4sU0KAXcH8W85jLd9C0varGXWFFD7axv-FjDEEQ8TL35Nh5svILn_hgMfB2TPNuixs","qi":"GgGk6GPOtfo2TFtuPQPVTTPGmEzoVekZNH9VQfvQchiRyU1cddYWGRzzJct1zP0GhRsam7m27zguxxVVOORjM5NAPHhjhuwmncmi5hZDyfyIURPXOgslPNG42XdIZdfJtgxqUuOhLNfeQcQXJM8S2EpauLmlm14blP5V-7ZOXO0"}'
JWKTESTPUBKEY1='{"e":"AQAB","kty":"RSA","n":"zMwIcZn24y3Aj-P5Vox-w54FkpoRGeYGhyF7rdDQN2bYO-8h09doVbbgstYauyZKRvk3iWoOwfY9foD0hHCJNtT20sqbx40osGN9qLERweO6Xn8adhVPN7isTT9KozdvsrOIBr7uQUsruvow4klIYrv5FqS_RHpy4f0CUlsjPqc3F5PC4yV0D0f_QUApr06--uHRdH3ucunvdwR1V1IZV0DEJwZ5DzEDQmynzo5oV1UVNb9DSzTXsUAzSipCrdIyUxCnofPp_PzKvqMbctBAchx0AKN8IK8Z3RGFYyrV3HxkXqFxZ4aTVnkXqlnGV5CRQhx59ckIWUxAlyLcGLXvmw"}'
# like JWKTESTKEY1 but with alg set
JWKTESTKEY2='{"alg": "RSA-OAEP","d":"pGBXnqbPbMR6PIPkyzz1OhmJq4ORmlHwh2GunXJuzSj1AhYL9rZ8fd_NNn128yPmllTN6LOBqNj1vqXOnMaeu63vdn08z8xTDlCsuUt2T0NzgQlPuducu8K0OURFqf-C3dIPqipxnWKydN7_gYEEYosxgKU3B8WolA65YFTaUxv-NQL-3rASUTtiQ1rtm2l-RBEIqOuFh350Bahnq_gtINxKpVahpLDiLTte6HpnbzU7ei_dW4v3j6foMg2pOWUAcfxNfmZwQO-eEge88E5WfN7HIQnBTTjAjrNwIP-SfaDmKpa37at1kTG932If0VopQ9CJZE_jM2wHx3VfZiTmAQ","dp":"RFUZdzAaCs3ak4lxptnHy5J_ujWgHk1CvzyIU1tEw1P9BCme-pW30YdEvXkXMzqiX8g0p6WdEvbfx0I9dctje9IbjCQcemxjIUx-2ifUppp8_I4BCaZ4K4puyt65TJL2za6PmyuVTDlugYceMIupmZ4bx6C70bjTeo1ErVe-yYE","dq":"zOCBbLcqCtkXUQqlmOEmb35GBc5HLV6LcQSYAm1mhMIRjK-cSiXAlg4yKhXoGNAuU-LBXyVLeOa4cNdG_v-34XZGmqIyBWG1ehmMumcblzI2-Cuj76jW26sWBvPBH7cyEf1FULS3acF-xPd8TkNA9P0laZmCshOfa_-zkMM5Tf8","e":"AQAB","kty":"RSA","n":"zMwIcZn24y3Aj-P5Vox-w54FkpoRGeYGhyF7rdDQN2bYO-8h09doVbbgstYauyZKRvk3iWoOwfY9foD0hHCJNtT20sqbx40osGN9qLERweO6Xn8adhVPN7isTT9KozdvsrOIBr7uQUsruvow4klIYrv5FqS_RHpy4f0CUlsjPqc3F5PC4yV0D0f_QUApr06--uHRdH3ucunvdwR1V1IZV0DEJwZ5DzEDQmynzo5oV1UVNb9DSzTXsUAzSipCrdIyUxCnofPp_PzKvqMbctBAchx0AKN8IK8Z3RGFYyrV3HxkXqFxZ4aTVnkXqlnGV5CRQhx59ckIWUxAlyLcGLXvmw","p":"_aCnqjENQBE-he_7XWBo7kXJHnOz6SucuLNPo35imTO4nJBkga9HOF8VxeM3OrskEFVudkDvSqbq4KtERiCGL8f3-LAUKSFaxULa0h9FPJOlks_JXVlDwGsXOyHirIHIEvvbjAAQlV_F7tQNCzSHuXmegh3yJWLwz6EcUw2z9YE","q":"zrZyXsm2jVHc9JkWEp8CMJ0J65f87KrYjQgcb46XkCK1E7bnFDLiNzYV-CQ8a9kKuWfd_LUx2FIjwrik5IFQXJA7Z7s4jvAh2J-pLutSD4sU0KAXcH8W85jLd9C0varGXWFFD7axv-FjDEEQ8TL35Nh5svILn_hgMfB2TPNuixs","qi":"GgGk6GPOtfo2TFtuPQPVTTPGmEzoVekZNH9VQfvQchiRyU1cddYWGRzzJct1zP0GhRsam7m27zguxxVVOORjM5NAPHhjhuwmncmi5hZDyfyIURPXOgslPNG42XdIZdfJtgxqUuOhLNfeQcQXJM8S2EpauLmlm14blP5V-7ZOXO0"}'
JWKTESTPUBKEY2='{"alg": "RSA-OAEP", "e":"AQAB","kty":"RSA","n":"zMwIcZn24y3Aj-P5Vox-w54FkpoRGeYGhyF7rdDQN2bYO-8h09doVbbgstYauyZKRvk3iWoOwfY9foD0hHCJNtT20sqbx40osGN9qLERweO6Xn8adhVPN7isTT9KozdvsrOIBr7uQUsruvow4klIYrv5FqS_RHpy4f0CUlsjPqc3F5PC4yV0D0f_QUApr06--uHRdH3ucunvdwR1V1IZV0DEJwZ5DzEDQmynzo5oV1UVNb9DSzTXsUAzSipCrdIyUxCnofPp_PzKvqMbctBAchx0AKN8IK8Z3RGFYyrV3HxkXqFxZ4aTVnkXqlnGV5CRQhx59ckIWUxAlyLcGLXvmw"}'
# like JWKTESTKEY1 but with alg set to RSA-OAEP-256
JWKTESTKEY3='{"alg": "RSA-OAEP-256","d":"pGBXnqbPbMR6PIPkyzz1OhmJq4ORmlHwh2GunXJuzSj1AhYL9rZ8fd_NNn128yPmllTN6LOBqNj1vqXOnMaeu63vdn08z8xTDlCsuUt2T0NzgQlPuducu8K0OURFqf-C3dIPqipxnWKydN7_gYEEYosxgKU3B8WolA65YFTaUxv-NQL-3rASUTtiQ1rtm2l-RBEIqOuFh350Bahnq_gtINxKpVahpLDiLTte6HpnbzU7ei_dW4v3j6foMg2pOWUAcfxNfmZwQO-eEge88E5WfN7HIQnBTTjAjrNwIP-SfaDmKpa37at1kTG932If0VopQ9CJZE_jM2wHx3VfZiTmAQ","dp":"RFUZdzAaCs3ak4lxptnHy5J_ujWgHk1CvzyIU1tEw1P9BCme-pW30YdEvXkXMzqiX8g0p6WdEvbfx0I9dctje9IbjCQcemxjIUx-2ifUppp8_I4BCaZ4K4puyt65TJL2za6PmyuVTDlugYceMIupmZ4bx6C70bjTeo1ErVe-yYE","dq":"zOCBbLcqCtkXUQqlmOEmb35GBc5HLV6LcQSYAm1mhMIRjK-cSiXAlg4yKhXoGNAuU-LBXyVLeOa4cNdG_v-34XZGmqIyBWG1ehmMumcblzI2-Cuj76jW26sWBvPBH7cyEf1FULS3acF-xPd8TkNA9P0laZmCshOfa_-zkMM5Tf8","e":"AQAB","kty":"RSA","n":"zMwIcZn24y3Aj-P5Vox-w54FkpoRGeYGhyF7rdDQN2bYO-8h09doVbbgstYauyZKRvk3iWoOwfY9foD0hHCJNtT20sqbx40osGN9qLERweO6Xn8adhVPN7isTT9KozdvsrOIBr7uQUsruvow4klIYrv5FqS_RHpy4f0CUlsjPqc3F5PC4yV0D0f_QUApr06--uHRdH3ucunvdwR1V1IZV0DEJwZ5DzEDQmynzo5oV1UVNb9DSzTXsUAzSipCrdIyUxCnofPp_PzKvqMbctBAchx0AKN8IK8Z3RGFYyrV3HxkXqFxZ4aTVnkXqlnGV5CRQhx59ckIWUxAlyLcGLXvmw","p":"_aCnqjENQBE-he_7XWBo7kXJHnOz6SucuLNPo35imTO4nJBkga9HOF8VxeM3OrskEFVudkDvSqbq4KtERiCGL8f3-LAUKSFaxULa0h9FPJOlks_JXVlDwGsXOyHirIHIEvvbjAAQlV_F7tQNCzSHuXmegh3yJWLwz6EcUw2z9YE","q":"zrZyXsm2jVHc9JkWEp8CMJ0J65f87KrYjQgcb46XkCK1E7bnFDLiNzYV-CQ8a9kKuWfd_LUx2FIjwrik5IFQXJA7Z7s4jvAh2J-pLutSD4sU0KAXcH8W85jLd9C0varGXWFFD7axv-FjDEEQ8TL35Nh5svILn_hgMfB2TPNuixs","qi":"GgGk6GPOtfo2TFtuPQPVTTPGmEzoVekZNH9VQfvQchiRyU1cddYWGRzzJct1zP0GhRsam7m27zguxxVVOORjM5NAPHhjhuwmncmi5hZDyfyIURPXOgslPNG42XdIZdfJtgxqUuOhLNfeQcQXJM8S2EpauLmlm14blP5V-7ZOXO0"}'
JWKTESTPUBKEY3='{"alg": "RSA-OAEP-256", "e":"AQAB","kty":"RSA","n":"zMwIcZn24y3Aj-P5Vox-w54FkpoRGeYGhyF7rdDQN2bYO-8h09doVbbgstYauyZKRvk3iWoOwfY9foD0hHCJNtT20sqbx40osGN9qLERweO6Xn8adhVPN7isTT9KozdvsrOIBr7uQUsruvow4klIYrv5FqS_RHpy4f0CUlsjPqc3F5PC4yV0D0f_QUApr06--uHRdH3ucunvdwR1V1IZV0DEJwZ5DzEDQmynzo5oV1UVNb9DSzTXsUAzSipCrdIyUxCnofPp_PzKvqMbctBAchx0AKN8IK8Z3RGFYyrV3HxkXqFxZ4aTVnkXqlnGV5CRQhx59ckIWUxAlyLcGLXvmw"}'
# EC key
JWKECTESTKEY1='{"kty":"EC","d":"ARwSGZOvkGPWdpftnk62s1dgapkHyPRg4Ey5dvn1Wucqs7xi4r930_d9BAmlnzRD3RgoE3wPLQhx_fJD18w1Ahec","use":"enc","crv":"P-521","kid":"bFYCcZ5aVw7LcnI8okm0cOSAnYw3mJ7r6jxCyxi93bc","x":"AZrywzluGZObiNdzRGGQNCmIfpy3k2iV8Z2dy_5QoF7aL5lCqBEmMMRHRTmP0XX-dS_nXrb9d-XcB53vBauNMars","y":"AIjZjRuVvl5jNk8PxqgmpJGFluyYC4PkTXZBb7HhzYaXWNcVVkCrGP5tmz2KkYYH9TKRAr564Q-wT13AzLVF9aj3","alg":"ECDH-ES+A128KW"}'
JWKECTESTPUBKEY1='{"kty":"EC","use":"enc","crv":"P-521","kid":"bFYCcZ5aVw7LcnI8okm0cOSAnYw3mJ7r6jxCyxi93bc","x":"AZrywzluGZObiNdzRGGQNCmIfpy3k2iV8Z2dy_5QoF7aL5lCqBEmMMRHRTmP0XX-dS_nXrb9d-XcB53vBauNMars","y":"AIjZjRuVvl5jNk8PxqgmpJGFluyYC4PkTXZBb7HhzYaXWNcVVkCrGP5tmz2KkYYH9TKRAr564Q-wT13AzLVF9aj3","alg":"ECDH-ES+A128KW"}'
trap "cleanup" EXIT QUIT
cleanup() {
if [ -n "$CONTAINERD_PID" ]; then
sudo kill -9 ${CONTAINERD_PID}
fi
if [ -n "${LOGFILE}" ]; then
sudo cat "${LOGFILE}"
fi
if [ -n "${WORKDIR}" ]; then
sudo rm -rf ${WORKDIR}
fi
@ -74,7 +91,8 @@ setup() {
startContainerd() {
cat <<_EOF_ >${CONFIG_TOML}
disable_plugins = ["cri"]
version = 2
disabled_plugins = ["io.containerd.grpc.v1.cri"]
root = "${ROOTDIR}"
state = "${STATEDIR}"
[grpc]
@ -88,6 +106,11 @@ state = "${STATEDIR}"
returns = "application/vnd.oci.image.layer.v1.tar+gzip"
path = "${BIN}/ctd-decoder"
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.zstd"]
accepts = ["application/vnd.oci.image.layer.v1.tar+zstd+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar+zstd"
path = "${BIN}/ctd-decoder"
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar"
@ -112,7 +135,8 @@ startContainerdLocalKeys() {
LOCAL_KEYS_PATH="${WORKDIR}/keys"
mkdir -p ${LOCAL_KEYS_PATH}
cat <<_EOF_ >${CONFIG_TOML}
disable_plugins = ["cri"]
version = 2
disabled_plugins = ["io.containerd.grpc.v1.cri"]
root = "${ROOTDIR}"
state = "${STATEDIR}"
[grpc]
@ -127,6 +151,12 @@ state = "${STATEDIR}"
path = "${BIN}/ctd-decoder"
args = ["--decryption-keys-path", "${LOCAL_KEYS_PATH}"]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.zstd"]
accepts = ["application/vnd.oci.image.layer.v1.tar+zsdt+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar+zstd"
path = "${BIN}/ctd-decoder"
args = ["--decryption-keys-path", "${LOCAL_KEYS_PATH}"]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar"
@ -162,13 +192,17 @@ pullImages() {
if [ -z "$IMAGE_PULL_CREDS" ]; then
echo "Note: Image pull credentials can be passed with env. variable IMAGE_PULL_CREDS=<username>:<password>"
fi
$CTR images rm --sync ${ALPINE_ENC} ${ALPINE_DEC} ${NGINX_ENC} ${NGINX_DEC} &>/dev/null
$CTR images pull ${IMAGE_PULL_CREDS:+--user ${IMAGE_PULL_CREDS}} --all-platforms ${ALPINE} &>/dev/null
$CTR images rm --sync ${ALPINE_ENC} ${ALPINE_DEC} ${NGINX_ENC} ${NGINX_DEC} ${BASH_ENC} &>/dev/null
$CTR content fetch ${IMAGE_PULL_CREDS:+--user ${IMAGE_PULL_CREDS}} --all-platforms ${ALPINE}
failExit $? "Image pull failed on ${ALPINE}"
$CTR images pull ${IMAGE_PULL_CREDS:+--user ${IMAGE_PULL_CREDS}} --platform linux/amd64 ${NGINX} &>/dev/null
failExit $? "Image pull failed on ${NGINX}"
# pull bash only for local platform
$CTR content fetch ${IMAGE_PULL_CREDS:+--user ${IMAGE_PULL_CREDS}} --all-platforms ${BASH} &>/dev/null
failExit $? "Image pull failed on ${BASH}"
LAYER_INFO_ALPINE="$($CTR images layerinfo ${ALPINE})"
failExit $? "Image layerinfo on plain image failed"
@ -278,6 +312,14 @@ testPGP() {
# remove ${ALPINE} and ${ALPINE_ENC} to clear cached and so we need to decrypt
$CTR images rm --sync ${ALPINE} ${ALPINE_ENC} &>/dev/null
# to avoid caching effects of the image we just decrypted and then exported,
# remove all snapshots to simulate a clean system for import
# The following is a brute-force removal ignoring dependencies
while [ -n "$($CTR snapshot ls | tail -n +2)" ]; do
for v in $($CTR snapshot ls | tail -n +2 | cut -d" " -f1); do
$CTR snapshot rm $v &>/dev/null
done
done
$CTR images import \
--all-platforms \
@ -411,10 +453,9 @@ testPGP() {
}
createJWEKeys() {
local rc
local rc traditional
echo "Generating keys for JWE encryption"
PRIVKEYPEM=${WORKDIR}/mykey.pem
PRIVKEYDER=${WORKDIR}/mykey.der
PRIVKEYPK8PEM=${WORKDIR}/mykeypk8.pem
@ -433,9 +474,20 @@ createJWEKeys() {
PRIVKEYJWK=${WORKDIR}/mykey.jwk
PUBKEYJWK=${WORKDIR}/mypubkey.jwk
PRIVKEY2JWK=${WORKDIR}/mykey2.jwk
PUBKEY2JWK=${WORKDIR}/mypubkey2.jwk
PRIVKEY3JWK=${WORKDIR}/mykey3.jwk
PUBKEY3JWK=${WORKDIR}/mypubkey3.jwk
ECPRIVKEYJWK=${WORKDIR}/my-ec-key.jwk
ECPUBKEYJWK=${WORKDIR}/my-ec-pubkey.jwk
ECPRIVKEYDER=${WORKDIR}/myeckey.der
ECPUBKEYDER=${WORKDIR}/myecpubkey.der
traditional=$(openssl genrsa --help 2>&1| sed -n 's/.*\(-traditional\).*/\1/p')
MSG="$(openssl genrsa -out ${PRIVKEYPEM} 2>&1)"
failExit $? "Could not generate private key\n$MSG"
@ -460,10 +512,10 @@ createJWEKeys() {
MSG="$(openssl rsa -inform pem -outform pem -pubout -in ${PRIVKEY2PEM} -out ${PUBKEY2PEM} 2>&1)"
failExit $? "Could not write 2nd public key in PEM format\n$MSG"
MSG="$(openssl genrsa -aes256 -passout pass:${PRIVKEY3PASSWORD} -out ${PRIVKEY3PASSPEM} 2>&1)"
MSG="$(openssl genrsa ${traditional} -aes256 -passout pass:${PRIVKEY3PASSWORD} -out ${PRIVKEY3PASSPEM} 2>&1)"
failExit $? "Could not generate 3rd private key\n$MSG"
MSG="$(openssl rsa -inform pem -outform pem -passin pass:${PRIVKEY3PASSWORD} -pubout -in ${PRIVKEY3PASSPEM} -out ${PUBKEY3PEM} 2>&1)"
MSG="$(openssl rsa ${traditional} -inform pem -outform pem -passin pass:${PRIVKEY3PASSWORD} -pubout -in ${PRIVKEY3PASSPEM} -out ${PUBKEY3PEM} 2>&1)"
failExit $? "Could not write 3rd public key in PEM format\n$MSG"
MSG="$(openssl ecparam -genkey -out ${ECPRIVKEYDER} -outform der -name secp521r1 2>&1)"
@ -483,6 +535,15 @@ createJWEKeys() {
echo "${JWKTESTKEY1}" >${PRIVKEYJWK}
echo "${JWKTESTPUBKEY1}" >${PUBKEYJWK}
echo "${JWKTESTKEY2}" >${PRIVKEY2JWK}
echo "${JWKTESTPUBKEY2}" >${PUBKEY2JWK}
echo "${JWKTESTKEY3}" >${PRIVKEY3JWK}
echo "${JWKTESTPUBKEY3}" >${PUBKEY3JWK}
echo "${JWKECTESTKEY1}" >${ECPRIVKEYJWK}
echo "${JWKECTESTPUBKEY1}" >${ECPUBKEYJWK}
}
testJWE() {
@ -572,55 +633,147 @@ testJWE() {
echo "Testing JWE encryption with a JWK"
# The JWK needs a separate test since it's a different key than the other ones
for recipient in jwe:${PUBKEYJWK}; do
# Test with RSA key
for recipient in jwe:${PUBKEYJWK} jwe:${PUBKEY2JWK} jwe:${PUBKEY3JWK}; do
$CTR images encrypt \
--recipient ${recipient} \
${ALPINE} ${ALPINE_ENC}
failExit $? "Image encryption with JWE failed; public key: ${recipient}"
failExit $? "Image encryption with JWE failed; RSA public key: ${recipient}"
LAYER_INFO_ALPINE_ENC="$($CTR images layerinfo ${ALPINE_ENC})"
failExit $? "Image layerinfo on JWE encrypted image failed; public key: ${recipient}"
failExit $? "Image layerinfo on JWE encrypted image failed; RSA public key: ${recipient}"
diff <(echo "${LAYER_INFO_ALPINE}" | gawk '{print $3}') \
<(echo "${LAYER_INFO_ALPINE_ENC}" | gawk '{print $3}')
failExit $? "Image layerinfo on JWE encrypted image shows differences in architectures"
failExit $? "Image layerinfo on JWE encrypted image shows differences in architectures (RSA)"
diff <(echo "${LAYER_INFO_ALPINE_ENC}" | gawk '{print $5}' | sort | uniq | tr -d '\n') \
<(echo -n "ENCRYPTIONjwe")
failExit $? "Image layerinfo on JWE encrypted image shows unexpected encryption"
failExit $? "Image layerinfo on JWE encrypted image shows unexpected encryption (RSA)"
for privkey in ${PRIVKEYJWK}; do
for privkey in ${PRIVKEYJWK} ${PRIVKEY2JWK} ${PRIVKEY3JWK}; do
$CTR images decrypt \
--key ${privkey} \
${ALPINE_ENC} ${ALPINE_DEC}
failExit $? "Image decryption with JWE failed: private key: ${privkey}"
failExit $? "Image decryption with JWE failed: RSA private key: ${privkey}"
LAYER_INFO_ALPINE_DEC="$($CTR images layerinfo ${ALPINE_DEC})"
failExit $? "Image layerinfo on decrypted image failed (JWE)"
failExit $? "Image layerinfo on decrypted image failed (JWE,RSA)"
diff <(echo "${LAYER_INFO_ALPINE}") <(echo "${LAYER_INFO_ALPINE_DEC}")
failExit $? "Image layerinfos are different (JWE)"
failExit $? "Image layerinfos are different (JWE,RSA)"
$CTR images rm --sync ${ALPINE_DEC} &>/dev/null
echo "Decryption with ${privkey} worked."
echo "Decryption with ${privkey} (RSA) worked."
done
$CTR images rm --sync ${ALPINE_ENC} &>/dev/null
echo "Encryption with ${recipient} worked"
echo "Encryption with ${recipient} (RSA) worked"
done
echo "PASS: JWE encryption with a JWK"
# Test with EC key
for recipient in jwe:${ECPUBKEYJWK}; do
$CTR images encrypt \
--recipient ${recipient} \
${ALPINE} ${ALPINE_ENC}
failExit $? "Image encryption with JWE failed; EC public key: ${recipient}"
LAYER_INFO_ALPINE_ENC="$($CTR images layerinfo ${ALPINE_ENC})"
failExit $? "Image layerinfo on JWE encrypted image failed; EC public key: ${recipient}"
diff <(echo "${LAYER_INFO_ALPINE}" | gawk '{print $3}') \
<(echo "${LAYER_INFO_ALPINE_ENC}" | gawk '{print $3}')
failExit $? "Image layerinfo on JWE encrypted image shows differences in architectures (EC)"
diff <(echo "${LAYER_INFO_ALPINE_ENC}" | gawk '{print $5}' | sort | uniq | tr -d '\n') \
<(echo -n "ENCRYPTIONjwe")
failExit $? "Image layerinfo on JWE encrypted image shows unexpected encryption (EC)"
for privkey in ${ECPRIVKEYJWK}; do
$CTR images decrypt \
--key ${privkey} \
${ALPINE_ENC} ${ALPINE_DEC}
failExit $? "Image decryption with JWE failed: EC private key: ${privkey}"
LAYER_INFO_ALPINE_DEC="$($CTR images layerinfo ${ALPINE_DEC})"
failExit $? "Image layerinfo on decrypted image failed (JWE,EC)"
diff <(echo "${LAYER_INFO_ALPINE}") <(echo "${LAYER_INFO_ALPINE_DEC}")
failExit $? "Image layerinfos are different (JWE,EC)"
$CTR images rm --sync ${ALPINE_DEC} &>/dev/null
echo "Decryption with ${privkey} (EC) worked."
done
$CTR images rm --sync ${ALPINE_ENC} &>/dev/null
echo "Encryption with ${recipient} (EC) worked"
done
echo "PASS: JWE encryption with JWK's (RSA & EC)"
$CTR images rm --sync ${ALPINE_DEC} ${ALPINE_ENC} &>/dev/null
echo
echo "Testing creation of container from encrypted image"
recipient=jwe:${PUBKEYJWK}
$CTR images encrypt \
--recipient ${recipient} \
${BASH} ${BASH_ENC}
failExit $? "Image encryption with JWE failed; public key: ${recipient}"
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# Create testcontainer1 from encrypted bash image ${BASH_ENC}
# Creating the container without providing (right) key must fail
MSG=$(sudo $CTR container create ${BASH_ENC} testcontainer1 2>&1)
if [ $? -eq 0 ]; then
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
failExit 1 "Should not have been able to create a container from encrypted image without passing keys"
fi
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# creating the container when providing right key must work
MSG=$(sudo bash -c "$CTR container create \
--key ${PRIVKEYJWK} \
${BASH_ENC} testcontainer1 2>&1")
failExit $? "Should have been able to create a container from encrypted image when passing keys\n${MSG}"
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# running the container without providing (right) key must fail
MSG=$(sudo bash -c "$CTR run \
--rm \
${BASH_ENC} testcontainer1 echo 'Hello world'" 2>&1)
if [ $? -eq 0 ]; then
MSG=$($CTR snapshot rm testcontainer1 2>&1)
failExit 1 "Should not have been able to run a container from encrypted image without passing keys"
fi
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# Running the container when providing right key must work
MSG=$(sudo bash -c "$CTR run \
--key ${PRIVKEYJWK} \
--rm \
${BASH_ENC} testcontainer1 echo 'Hello world'" 2>&1)
failExit $? "Should have been able to run a container from encrypted image when passing keys\n${MSG}"
$CTR images rm --sync ${BASH_ENC} &>/dev/null
echo "PASS: Creation of container from encrypted image"
echo
}
testLocalKeys() {
createJWEKeys
setupPKCS11
# Env. variable needed for encryption with SOFTHSM_KEY_PEM
export OCICRYPT_OAEP_HASHALG=sha1
echo "Testing JWE and PKCS11 type of encryption with local unpack keys"
# Remove original images
$CTR images rm --sync ${ALPINE_ENC} ${ALPINE_DEC} ${NGINX_ENC} ${NGINX_DEC} &>/dev/null
# Remove existing images
$CTR images rm --sync ${ALPINE_ENC} ${ALPINE_DEC} ${NGINX_ENC} ${NGINX_DEC} ${BASH_ENC} &>/dev/null
local recipient1=jwe:${PUBKEYPEM}
local recipient2=pkcs11:${SOFTHSM_KEY}
@ -663,7 +816,7 @@ testLocalKeys() {
echo "Testing creation of container from encrypted image with local keys (JWE)"
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
MSG=$(sudo $CTR container create ${ALPINE_ENC} --skip-decrypt-auth --key ${PRIVKEY2PEM} testcontainer1 2>&1)
MSG=$(sudo $CTR container create --skip-decrypt-auth ${ALPINE_ENC} testcontainer1 2>&1)
failExit $? "Should have been able to create a container from encrypted image when local keys exists (JWE)\n${MSG}"
MSG=$($CTR container rm testcontainer1 2>&1)
@ -679,7 +832,7 @@ testLocalKeys() {
echo "Testing creation of container from encrypted image with local keys (PKCS11)"
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
MSG=$(sudo $CTR container create ${ALPINE_ENC} --skip-decrypt-auth --key ${PRIVKEY2PEM} testcontainer1 2>&1)
MSG=$(sudo $CTR container create --skip-decrypt-auth ${ALPINE_ENC} testcontainer1 2>&1)
failExit $? "Should have been able to create a container from encrypted image when local keys exists (PKCS11)\n${MSG}"
MSG=$($CTR container rm testcontainer1 2>&1)
@ -690,6 +843,65 @@ testLocalKeys() {
echo "PASS: JWE and PKCS11 type of encryption with local unpack keys"
echo
rm -f ${LOCAL_KEYS_PATH}/*
echo "Testing creation of container from encrypted image with local key (JWK)"
recipient=jwe:${PUBKEYJWK}
$CTR images encrypt \
--recipient ${recipient} \
${BASH} ${BASH_ENC}
failExit $? "Image encryption with JWE failed; public key: ${recipient}"
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# Create testcontainer1 from encrypted bash image ${BASH_ENC}
# Creating the container without providing (right) key must fail
MSG=$(sudo $CTR container create ${BASH_ENC} testcontainer1 2>&1)
if [ $? -eq 0 ]; then
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
failExit 1 "Should not have been able to create a container from encrypted image when JWK key file is not available"
fi
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# creating the container when providing right key must work
cp ${PRIVKEYJWK} ${LOCAL_KEYS_PATH}/.
MSG=$(sudo bash -c "$CTR container create --skip-decrypt-auth ${BASH_ENC} testcontainer1 2>&1")
failExit $? "Should have been able to create a container from encrypted image when JWK key file is available\n${MSG}"
MSG=$($CTR container rm testcontainer1 2>&1)
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# Running the container without providing (right) key must fail.
# If we were not to pass --skip-decrypt-auth then this test would fail since then
# authorization will fail since no keys are provided via command line that ctr-enc
# could do authorization with (on client side!). To make running the image fail we
# don't pass --skip-decrypt-auth.
rm -f ${LOCAL_KEYS_PATH}/*
MSG=$(sudo bash -c "$CTR run \
--rm \
${BASH_ENC} testcontainer1 echo 'Hello world'" 2>&1)
if [ $? -eq 0 ]; then
MSG=$($CTR snapshot rm testcontainer1 2>&1)
failExit 1 "Should not have been able to run a container from encrypted image when JWK key file is not available"
fi
MSG=$($CTR snapshot rm testcontainer1 2>&1)
# Running the container when providing right key must work
# This only works if --skip-decrypt-auth is passed since no keys are provided
# on the command line and ctr-enc would otherwise do authorization
cp ${PRIVKEYJWK} ${LOCAL_KEYS_PATH}/.
MSG=$(sudo bash -c "$CTR run \
--rm \
--skip-decrypt-auth \
${BASH_ENC} testcontainer1 echo 'Hello world'" 2>&1)
failExit $? "Should have been able to run a container from encrypted image when JWK key file is available\n${MSG}"
$CTR images rm --sync ${BASH_ENC} &>/dev/null
echo "PASS: Creation of container from encrypted image with local JWK key"
}
setupPKCS7() {
@ -1143,6 +1355,11 @@ setupKeyprovider() {
_EOF_
}
testKeyproviderInvalidPath() {
export OCICRYPT_KEYPROVIDER_CONFIG=/path/to/nowhere
testJWE
}
testKeyprovider() {
if [ -z "${KEYPROVIDER}" ]; then
echo "Skipping keyprovider test; require KEYPROVIDER to point to executable"
@ -1157,7 +1374,7 @@ testKeyprovider() {
echo "Testing keyprovider using '${KEYPROVIDER}'"
echo "Testing large recpient list"
echo "Testing single recipient list"
$CTR images encrypt \
--recipient provider:testkeyprovider:foobar \
@ -1174,7 +1391,7 @@ testKeyprovider() {
<(echo -n "ENCRYPTIONprovider.testkeyprovider")
failExit $? "Image layerinfo on keyprovider encrypted image shows unexpected encryption"
MSG=$(sudo $CTR container create ${ALPINE_ENC} --skip-decrypt-auth --key provider:testkeyprovider:xyz testcontainer1 2>&1)
MSG=$(sudo $CTR container create --skip-decrypt-auth --key provider:testkeyprovider:xyz ${ALPINE_ENC} testcontainer1 2>&1)
failExit $? "Should have been able to create a container from encrypted (keyprovider)\n${MSG}"
@ -1240,6 +1457,7 @@ testPKCS7
testPKCS11
testPGPandJWEandPKCS7andPKCS11andKeyprovider
testKeyprovider
testKeyproviderInvalidPath
cleanup
# Test containerd with flow where keys are in local directory

View File

@ -1,5 +0,0 @@
TAGS
tags
.*.swp
tomlcheck/tomlcheck
toml.test

View File

@ -1,15 +0,0 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- tip
install:
- go install ./...
- go get github.com/BurntSushi/toml-test
script:
- export PATH="$PATH:$HOME/gopath/bin"
- make test

View File

@ -1,3 +0,0 @@
Compatible with TOML version
[v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md)

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 TOML authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,19 +0,0 @@
install:
go install ./...
test: install
go test -v
toml-test toml-test-decoder
toml-test -encoder toml-test-encoder
fmt:
gofmt -w *.go */*.go
colcheck *.go */*.go
tags:
find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS
push:
git push origin master
git push github master

View File

@ -1,218 +0,0 @@
## TOML parser and encoder for Go with reflection
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
reflection interface similar to Go's standard library `json` and `xml`
packages. This package also supports the `encoding.TextUnmarshaler` and
`encoding.TextMarshaler` interfaces so that you can define custom data
representations. (There is an example of this below.)
Spec: https://github.com/toml-lang/toml
Compatible with TOML version
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
Documentation: https://godoc.org/github.com/BurntSushi/toml
Installation:
```bash
go get github.com/BurntSushi/toml
```
Try the toml validator:
```bash
go get github.com/BurntSushi/toml/cmd/tomlv
tomlv some-toml-file.toml
```
[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml)
### Testing
This package passes all tests in
[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder
and the encoder.
### Examples
This package works similarly to how the Go standard library handles `XML`
and `JSON`. Namely, data is loaded into Go values via reflection.
For the simplest example, consider some TOML file as just a list of keys
and values:
```toml
Age = 25
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z
```
Which could be defined in Go as:
```go
type Config struct {
Age int
Cats []string
Pi float64
Perfection []int
DOB time.Time // requires `import time`
}
```
And then decoded with:
```go
var conf Config
if _, err := toml.Decode(tomlData, &conf); err != nil {
// handle error
}
```
You can also use struct tags if your struct field name doesn't map to a TOML
key value directly:
```toml
some_key_NAME = "wat"
```
```go
type TOML struct {
ObscureKey string `toml:"some_key_NAME"`
}
```
### Using the `encoding.TextUnmarshaler` interface
Here's an example that automatically parses duration strings into
`time.Duration` values:
```toml
[[song]]
name = "Thunder Road"
duration = "4m49s"
[[song]]
name = "Stairway to Heaven"
duration = "8m03s"
```
Which can be decoded with:
```go
type song struct {
Name string
Duration duration
}
type songs struct {
Song []song
}
var favorites songs
if _, err := toml.Decode(blob, &favorites); err != nil {
log.Fatal(err)
}
for _, s := range favorites.Song {
fmt.Printf("%s (%s)\n", s.Name, s.Duration)
}
```
And you'll also need a `duration` type that satisfies the
`encoding.TextUnmarshaler` interface:
```go
type duration struct {
time.Duration
}
func (d *duration) UnmarshalText(text []byte) error {
var err error
d.Duration, err = time.ParseDuration(string(text))
return err
}
```
### More complex usage
Here's an example of how to load the example from the official spec page:
```toml
# This is a TOML document. Boom.
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]
```
And the corresponding Go types are:
```go
type tomlConfig struct {
Title string
Owner ownerInfo
DB database `toml:"database"`
Servers map[string]server
Clients clients
}
type ownerInfo struct {
Name string
Org string `toml:"organization"`
Bio string
DOB time.Time
}
type database struct {
Server string
Ports []int
ConnMax int `toml:"connection_max"`
Enabled bool
}
type server struct {
IP string
DC string
}
type clients struct {
Data [][]interface{}
Hosts []string
}
```
Note that a case insensitive match will be tried if an exact match can't be
found.
A working example of the above can be found in `_examples/example.{go,toml}`.

View File

@ -1,509 +0,0 @@
package toml
import (
"fmt"
"io"
"io/ioutil"
"math"
"reflect"
"strings"
"time"
)
func e(format string, args ...interface{}) error {
return fmt.Errorf("toml: "+format, args...)
}
// Unmarshaler is the interface implemented by objects that can unmarshal a
// TOML description of themselves.
type Unmarshaler interface {
UnmarshalTOML(interface{}) error
}
// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
func Unmarshal(p []byte, v interface{}) error {
_, err := Decode(string(p), v)
return err
}
// Primitive is a TOML value that hasn't been decoded into a Go value.
// When using the various `Decode*` functions, the type `Primitive` may
// be given to any value, and its decoding will be delayed.
//
// A `Primitive` value can be decoded using the `PrimitiveDecode` function.
//
// The underlying representation of a `Primitive` value is subject to change.
// Do not rely on it.
//
// N.B. Primitive values are still parsed, so using them will only avoid
// the overhead of reflection. They can be useful when you don't know the
// exact type of TOML data until run time.
type Primitive struct {
undecoded interface{}
context Key
}
// DEPRECATED!
//
// Use MetaData.PrimitiveDecode instead.
func PrimitiveDecode(primValue Primitive, v interface{}) error {
md := MetaData{decoded: make(map[string]bool)}
return md.unify(primValue.undecoded, rvalue(v))
}
// PrimitiveDecode is just like the other `Decode*` functions, except it
// decodes a TOML value that has already been parsed. Valid primitive values
// can *only* be obtained from values filled by the decoder functions,
// including this method. (i.e., `v` may contain more `Primitive`
// values.)
//
// Meta data for primitive values is included in the meta data returned by
// the `Decode*` functions with one exception: keys returned by the Undecoded
// method will only reflect keys that were decoded. Namely, any keys hidden
// behind a Primitive will be considered undecoded. Executing this method will
// update the undecoded keys in the meta data. (See the example.)
func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
md.context = primValue.context
defer func() { md.context = nil }()
return md.unify(primValue.undecoded, rvalue(v))
}
// Decode will decode the contents of `data` in TOML format into a pointer
// `v`.
//
// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
// used interchangeably.)
//
// TOML arrays of tables correspond to either a slice of structs or a slice
// of maps.
//
// TOML datetimes correspond to Go `time.Time` values.
//
// All other TOML types (float, string, int, bool and array) correspond
// to the obvious Go types.
//
// An exception to the above rules is if a type implements the
// encoding.TextUnmarshaler interface. In this case, any primitive TOML value
// (floats, strings, integers, booleans and datetimes) will be converted to
// a byte string and given to the value's UnmarshalText method. See the
// Unmarshaler example for a demonstration with time duration strings.
//
// Key mapping
//
// TOML keys can map to either keys in a Go map or field names in a Go
// struct. The special `toml` struct tag may be used to map TOML keys to
// struct fields that don't match the key name exactly. (See the example.)
// A case insensitive match to struct names will be tried if an exact match
// can't be found.
//
// The mapping between TOML values and Go values is loose. That is, there
// may exist TOML values that cannot be placed into your representation, and
// there may be parts of your representation that do not correspond to
// TOML values. This loose mapping can be made stricter by using the IsDefined
// and/or Undecoded methods on the MetaData returned.
//
// This decoder will not handle cyclic types. If a cyclic type is passed,
// `Decode` will not terminate.
func Decode(data string, v interface{}) (MetaData, error) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr {
return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
}
if rv.IsNil() {
return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
}
p, err := parse(data)
if err != nil {
return MetaData{}, err
}
md := MetaData{
p.mapping, p.types, p.ordered,
make(map[string]bool, len(p.ordered)), nil,
}
return md, md.unify(p.mapping, indirect(rv))
}
// DecodeFile is just like Decode, except it will automatically read the
// contents of the file at `fpath` and decode it for you.
func DecodeFile(fpath string, v interface{}) (MetaData, error) {
bs, err := ioutil.ReadFile(fpath)
if err != nil {
return MetaData{}, err
}
return Decode(string(bs), v)
}
// DecodeReader is just like Decode, except it will consume all bytes
// from the reader and decode it for you.
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
bs, err := ioutil.ReadAll(r)
if err != nil {
return MetaData{}, err
}
return Decode(string(bs), v)
}
// unify performs a sort of type unification based on the structure of `rv`,
// which is the client representation.
//
// Any type mismatch produces an error. Finding a type that we don't know
// how to handle produces an unsupported type error.
func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
// Special case. Look for a `Primitive` value.
if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
// Save the undecoded data and the key context into the primitive
// value.
context := make(Key, len(md.context))
copy(context, md.context)
rv.Set(reflect.ValueOf(Primitive{
undecoded: data,
context: context,
}))
return nil
}
// Special case. Unmarshaler Interface support.
if rv.CanAddr() {
if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
return v.UnmarshalTOML(data)
}
}
// Special case. Handle time.Time values specifically.
// TODO: Remove this code when we decide to drop support for Go 1.1.
// This isn't necessary in Go 1.2 because time.Time satisfies the encoding
// interfaces.
if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) {
return md.unifyDatetime(data, rv)
}
// Special case. Look for a value satisfying the TextUnmarshaler interface.
if v, ok := rv.Interface().(TextUnmarshaler); ok {
return md.unifyText(data, v)
}
// BUG(burntsushi)
// The behavior here is incorrect whenever a Go type satisfies the
// encoding.TextUnmarshaler interface but also corresponds to a TOML
// hash or array. In particular, the unmarshaler should only be applied
// to primitive TOML values. But at this point, it will be applied to
// all kinds of values and produce an incorrect error whenever those values
// are hashes or arrays (including arrays of tables).
k := rv.Kind()
// laziness
if k >= reflect.Int && k <= reflect.Uint64 {
return md.unifyInt(data, rv)
}
switch k {
case reflect.Ptr:
elem := reflect.New(rv.Type().Elem())
err := md.unify(data, reflect.Indirect(elem))
if err != nil {
return err
}
rv.Set(elem)
return nil
case reflect.Struct:
return md.unifyStruct(data, rv)
case reflect.Map:
return md.unifyMap(data, rv)
case reflect.Array:
return md.unifyArray(data, rv)
case reflect.Slice:
return md.unifySlice(data, rv)
case reflect.String:
return md.unifyString(data, rv)
case reflect.Bool:
return md.unifyBool(data, rv)
case reflect.Interface:
// we only support empty interfaces.
if rv.NumMethod() > 0 {
return e("unsupported type %s", rv.Type())
}
return md.unifyAnything(data, rv)
case reflect.Float32:
fallthrough
case reflect.Float64:
return md.unifyFloat64(data, rv)
}
return e("unsupported type %s", rv.Kind())
}
func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
tmap, ok := mapping.(map[string]interface{})
if !ok {
if mapping == nil {
return nil
}
return e("type mismatch for %s: expected table but found %T",
rv.Type().String(), mapping)
}
for key, datum := range tmap {
var f *field
fields := cachedTypeFields(rv.Type())
for i := range fields {
ff := &fields[i]
if ff.name == key {
f = ff
break
}
if f == nil && strings.EqualFold(ff.name, key) {
f = ff
}
}
if f != nil {
subv := rv
for _, i := range f.index {
subv = indirect(subv.Field(i))
}
if isUnifiable(subv) {
md.decoded[md.context.add(key).String()] = true
md.context = append(md.context, key)
if err := md.unify(datum, subv); err != nil {
return err
}
md.context = md.context[0 : len(md.context)-1]
} else if f.name != "" {
// Bad user! No soup for you!
return e("cannot write unexported field %s.%s",
rv.Type().String(), f.name)
}
}
}
return nil
}
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
tmap, ok := mapping.(map[string]interface{})
if !ok {
if tmap == nil {
return nil
}
return badtype("map", mapping)
}
if rv.IsNil() {
rv.Set(reflect.MakeMap(rv.Type()))
}
for k, v := range tmap {
md.decoded[md.context.add(k).String()] = true
md.context = append(md.context, k)
rvkey := indirect(reflect.New(rv.Type().Key()))
rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
if err := md.unify(v, rvval); err != nil {
return err
}
md.context = md.context[0 : len(md.context)-1]
rvkey.SetString(k)
rv.SetMapIndex(rvkey, rvval)
}
return nil
}
func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
datav := reflect.ValueOf(data)
if datav.Kind() != reflect.Slice {
if !datav.IsValid() {
return nil
}
return badtype("slice", data)
}
sliceLen := datav.Len()
if sliceLen != rv.Len() {
return e("expected array length %d; got TOML array of length %d",
rv.Len(), sliceLen)
}
return md.unifySliceArray(datav, rv)
}
func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
datav := reflect.ValueOf(data)
if datav.Kind() != reflect.Slice {
if !datav.IsValid() {
return nil
}
return badtype("slice", data)
}
n := datav.Len()
if rv.IsNil() || rv.Cap() < n {
rv.Set(reflect.MakeSlice(rv.Type(), n, n))
}
rv.SetLen(n)
return md.unifySliceArray(datav, rv)
}
func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
sliceLen := data.Len()
for i := 0; i < sliceLen; i++ {
v := data.Index(i).Interface()
sliceval := indirect(rv.Index(i))
if err := md.unify(v, sliceval); err != nil {
return err
}
}
return nil
}
func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error {
if _, ok := data.(time.Time); ok {
rv.Set(reflect.ValueOf(data))
return nil
}
return badtype("time.Time", data)
}
func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
if s, ok := data.(string); ok {
rv.SetString(s)
return nil
}
return badtype("string", data)
}
func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
if num, ok := data.(float64); ok {
switch rv.Kind() {
case reflect.Float32:
fallthrough
case reflect.Float64:
rv.SetFloat(num)
default:
panic("bug")
}
return nil
}
return badtype("float", data)
}
func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
if num, ok := data.(int64); ok {
if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
switch rv.Kind() {
case reflect.Int, reflect.Int64:
// No bounds checking necessary.
case reflect.Int8:
if num < math.MinInt8 || num > math.MaxInt8 {
return e("value %d is out of range for int8", num)
}
case reflect.Int16:
if num < math.MinInt16 || num > math.MaxInt16 {
return e("value %d is out of range for int16", num)
}
case reflect.Int32:
if num < math.MinInt32 || num > math.MaxInt32 {
return e("value %d is out of range for int32", num)
}
}
rv.SetInt(num)
} else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
unum := uint64(num)
switch rv.Kind() {
case reflect.Uint, reflect.Uint64:
// No bounds checking necessary.
case reflect.Uint8:
if num < 0 || unum > math.MaxUint8 {
return e("value %d is out of range for uint8", num)
}
case reflect.Uint16:
if num < 0 || unum > math.MaxUint16 {
return e("value %d is out of range for uint16", num)
}
case reflect.Uint32:
if num < 0 || unum > math.MaxUint32 {
return e("value %d is out of range for uint32", num)
}
}
rv.SetUint(unum)
} else {
panic("unreachable")
}
return nil
}
return badtype("integer", data)
}
func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
if b, ok := data.(bool); ok {
rv.SetBool(b)
return nil
}
return badtype("boolean", data)
}
func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
rv.Set(reflect.ValueOf(data))
return nil
}
func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error {
var s string
switch sdata := data.(type) {
case TextMarshaler:
text, err := sdata.MarshalText()
if err != nil {
return err
}
s = string(text)
case fmt.Stringer:
s = sdata.String()
case string:
s = sdata
case bool:
s = fmt.Sprintf("%v", sdata)
case int64:
s = fmt.Sprintf("%d", sdata)
case float64:
s = fmt.Sprintf("%f", sdata)
default:
return badtype("primitive (string-like)", data)
}
if err := v.UnmarshalText([]byte(s)); err != nil {
return err
}
return nil
}
// rvalue returns a reflect.Value of `v`. All pointers are resolved.
func rvalue(v interface{}) reflect.Value {
return indirect(reflect.ValueOf(v))
}
// indirect returns the value pointed to by a pointer.
// Pointers are followed until the value is not a pointer.
// New values are allocated for each nil pointer.
//
// An exception to this rule is if the value satisfies an interface of
// interest to us (like encoding.TextUnmarshaler).
func indirect(v reflect.Value) reflect.Value {
if v.Kind() != reflect.Ptr {
if v.CanSet() {
pv := v.Addr()
if _, ok := pv.Interface().(TextUnmarshaler); ok {
return pv
}
}
return v
}
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
return indirect(reflect.Indirect(v))
}
func isUnifiable(rv reflect.Value) bool {
if rv.CanSet() {
return true
}
if _, ok := rv.Interface().(TextUnmarshaler); ok {
return true
}
return false
}
func badtype(expected string, data interface{}) error {
return e("cannot load TOML value of type %T into a Go %s", data, expected)
}

View File

@ -1,121 +0,0 @@
package toml
import "strings"
// MetaData allows access to meta information about TOML data that may not
// be inferrable via reflection. In particular, whether a key has been defined
// and the TOML type of a key.
type MetaData struct {
mapping map[string]interface{}
types map[string]tomlType
keys []Key
decoded map[string]bool
context Key // Used only during decoding.
}
// IsDefined returns true if the key given exists in the TOML data. The key
// should be specified hierarchially. e.g.,
//
// // access the TOML key 'a.b.c'
// IsDefined("a", "b", "c")
//
// IsDefined will return false if an empty key given. Keys are case sensitive.
func (md *MetaData) IsDefined(key ...string) bool {
if len(key) == 0 {
return false
}
var hash map[string]interface{}
var ok bool
var hashOrVal interface{} = md.mapping
for _, k := range key {
if hash, ok = hashOrVal.(map[string]interface{}); !ok {
return false
}
if hashOrVal, ok = hash[k]; !ok {
return false
}
}
return true
}
// Type returns a string representation of the type of the key specified.
//
// Type will return the empty string if given an empty key or a key that
// does not exist. Keys are case sensitive.
func (md *MetaData) Type(key ...string) string {
fullkey := strings.Join(key, ".")
if typ, ok := md.types[fullkey]; ok {
return typ.typeString()
}
return ""
}
// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
// to get values of this type.
type Key []string
func (k Key) String() string {
return strings.Join(k, ".")
}
func (k Key) maybeQuotedAll() string {
var ss []string
for i := range k {
ss = append(ss, k.maybeQuoted(i))
}
return strings.Join(ss, ".")
}
func (k Key) maybeQuoted(i int) string {
quote := false
for _, c := range k[i] {
if !isBareKeyChar(c) {
quote = true
break
}
}
if quote {
return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
}
return k[i]
}
func (k Key) add(piece string) Key {
newKey := make(Key, len(k)+1)
copy(newKey, k)
newKey[len(k)] = piece
return newKey
}
// Keys returns a slice of every key in the TOML data, including key groups.
// Each key is itself a slice, where the first element is the top of the
// hierarchy and the last is the most specific.
//
// The list will have the same order as the keys appeared in the TOML data.
//
// All keys returned are non-empty.
func (md *MetaData) Keys() []Key {
return md.keys
}
// Undecoded returns all keys that have not been decoded in the order in which
// they appear in the original TOML document.
//
// This includes keys that haven't been decoded because of a Primitive value.
// Once the Primitive value is decoded, the keys will be considered decoded.
//
// Also note that decoding into an empty interface will result in no decoding,
// and so no keys will be considered decoded.
//
// In this sense, the Undecoded keys correspond to keys in the TOML document
// that do not have a concrete type in your representation.
func (md *MetaData) Undecoded() []Key {
undecoded := make([]Key, 0, len(md.keys))
for _, key := range md.keys {
if !md.decoded[key.String()] {
undecoded = append(undecoded, key)
}
}
return undecoded
}

View File

@ -1,27 +0,0 @@
/*
Package toml provides facilities for decoding and encoding TOML configuration
files via reflection. There is also support for delaying decoding with
the Primitive type, and querying the set of keys in a TOML document with the
MetaData type.
The specification implemented: https://github.com/toml-lang/toml
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
whether a file is a valid TOML document. It can also be used to print the
type of each key in a TOML document.
Testing
There are two important types of tests used for this package. The first is
contained inside '*_test.go' files and uses the standard Go unit testing
framework. These tests are primarily devoted to holistically testing the
decoder and encoder.
The second type of testing is used to verify the implementation's adherence
to the TOML specification. These tests have been factored into their own
project: https://github.com/BurntSushi/toml-test
The reason the tests are in a separate project is so that they can be used by
any implementation of TOML. Namely, it is language agnostic.
*/
package toml

View File

@ -1,568 +0,0 @@
package toml
import (
"bufio"
"errors"
"fmt"
"io"
"reflect"
"sort"
"strconv"
"strings"
"time"
)
type tomlEncodeError struct{ error }
var (
errArrayMixedElementTypes = errors.New(
"toml: cannot encode array with mixed element types")
errArrayNilElement = errors.New(
"toml: cannot encode array with nil element")
errNonString = errors.New(
"toml: cannot encode a map with non-string key type")
errAnonNonStruct = errors.New(
"toml: cannot encode an anonymous field that is not a struct")
errArrayNoTable = errors.New(
"toml: TOML array element cannot contain a table")
errNoKey = errors.New(
"toml: top-level values must be Go maps or structs")
errAnything = errors.New("") // used in testing
)
var quotedReplacer = strings.NewReplacer(
"\t", "\\t",
"\n", "\\n",
"\r", "\\r",
"\"", "\\\"",
"\\", "\\\\",
)
// Encoder controls the encoding of Go values to a TOML document to some
// io.Writer.
//
// The indentation level can be controlled with the Indent field.
type Encoder struct {
// A single indentation level. By default it is two spaces.
Indent string
// hasWritten is whether we have written any output to w yet.
hasWritten bool
w *bufio.Writer
}
// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
// given. By default, a single indentation level is 2 spaces.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{
w: bufio.NewWriter(w),
Indent: " ",
}
}
// Encode writes a TOML representation of the Go value to the underlying
// io.Writer. If the value given cannot be encoded to a valid TOML document,
// then an error is returned.
//
// The mapping between Go values and TOML values should be precisely the same
// as for the Decode* functions. Similarly, the TextMarshaler interface is
// supported by encoding the resulting bytes as strings. (If you want to write
// arbitrary binary data then you will need to use something like base64 since
// TOML does not have any binary types.)
//
// When encoding TOML hashes (i.e., Go maps or structs), keys without any
// sub-hashes are encoded first.
//
// If a Go map is encoded, then its keys are sorted alphabetically for
// deterministic output. More control over this behavior may be provided if
// there is demand for it.
//
// Encoding Go values without a corresponding TOML representation---like map
// types with non-string keys---will cause an error to be returned. Similarly
// for mixed arrays/slices, arrays/slices with nil elements, embedded
// non-struct types and nested slices containing maps or structs.
// (e.g., [][]map[string]string is not allowed but []map[string]string is OK
// and so is []map[string][]string.)
func (enc *Encoder) Encode(v interface{}) error {
rv := eindirect(reflect.ValueOf(v))
if err := enc.safeEncode(Key([]string{}), rv); err != nil {
return err
}
return enc.w.Flush()
}
func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
defer func() {
if r := recover(); r != nil {
if terr, ok := r.(tomlEncodeError); ok {
err = terr.error
return
}
panic(r)
}
}()
enc.encode(key, rv)
return nil
}
func (enc *Encoder) encode(key Key, rv reflect.Value) {
// Special case. Time needs to be in ISO8601 format.
// Special case. If we can marshal the type to text, then we used that.
// Basically, this prevents the encoder for handling these types as
// generic structs (or whatever the underlying type of a TextMarshaler is).
switch rv.Interface().(type) {
case time.Time, TextMarshaler:
enc.keyEqElement(key, rv)
return
}
k := rv.Kind()
switch k {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64,
reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
enc.keyEqElement(key, rv)
case reflect.Array, reflect.Slice:
if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
enc.eArrayOfTables(key, rv)
} else {
enc.keyEqElement(key, rv)
}
case reflect.Interface:
if rv.IsNil() {
return
}
enc.encode(key, rv.Elem())
case reflect.Map:
if rv.IsNil() {
return
}
enc.eTable(key, rv)
case reflect.Ptr:
if rv.IsNil() {
return
}
enc.encode(key, rv.Elem())
case reflect.Struct:
enc.eTable(key, rv)
default:
panic(e("unsupported type for key '%s': %s", key, k))
}
}
// eElement encodes any value that can be an array element (primitives and
// arrays).
func (enc *Encoder) eElement(rv reflect.Value) {
switch v := rv.Interface().(type) {
case time.Time:
// Special case time.Time as a primitive. Has to come before
// TextMarshaler below because time.Time implements
// encoding.TextMarshaler, but we need to always use UTC.
enc.wf(v.UTC().Format("2006-01-02T15:04:05Z"))
return
case TextMarshaler:
// Special case. Use text marshaler if it's available for this value.
if s, err := v.MarshalText(); err != nil {
encPanic(err)
} else {
enc.writeQuoted(string(s))
}
return
}
switch rv.Kind() {
case reflect.Bool:
enc.wf(strconv.FormatBool(rv.Bool()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64:
enc.wf(strconv.FormatInt(rv.Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64:
enc.wf(strconv.FormatUint(rv.Uint(), 10))
case reflect.Float32:
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
case reflect.Float64:
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
case reflect.Array, reflect.Slice:
enc.eArrayOrSliceElement(rv)
case reflect.Interface:
enc.eElement(rv.Elem())
case reflect.String:
enc.writeQuoted(rv.String())
default:
panic(e("unexpected primitive type: %s", rv.Kind()))
}
}
// By the TOML spec, all floats must have a decimal with at least one
// number on either side.
func floatAddDecimal(fstr string) string {
if !strings.Contains(fstr, ".") {
return fstr + ".0"
}
return fstr
}
func (enc *Encoder) writeQuoted(s string) {
enc.wf("\"%s\"", quotedReplacer.Replace(s))
}
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
length := rv.Len()
enc.wf("[")
for i := 0; i < length; i++ {
elem := rv.Index(i)
enc.eElement(elem)
if i != length-1 {
enc.wf(", ")
}
}
enc.wf("]")
}
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
if len(key) == 0 {
encPanic(errNoKey)
}
for i := 0; i < rv.Len(); i++ {
trv := rv.Index(i)
if isNil(trv) {
continue
}
panicIfInvalidKey(key)
enc.newline()
enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
enc.newline()
enc.eMapOrStruct(key, trv)
}
}
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
panicIfInvalidKey(key)
if len(key) == 1 {
// Output an extra newline between top-level tables.
// (The newline isn't written if nothing else has been written though.)
enc.newline()
}
if len(key) > 0 {
enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
enc.newline()
}
enc.eMapOrStruct(key, rv)
}
func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
switch rv := eindirect(rv); rv.Kind() {
case reflect.Map:
enc.eMap(key, rv)
case reflect.Struct:
enc.eStruct(key, rv)
default:
panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
}
}
func (enc *Encoder) eMap(key Key, rv reflect.Value) {
rt := rv.Type()
if rt.Key().Kind() != reflect.String {
encPanic(errNonString)
}
// Sort keys so that we have deterministic output. And write keys directly
// underneath this key first, before writing sub-structs or sub-maps.
var mapKeysDirect, mapKeysSub []string
for _, mapKey := range rv.MapKeys() {
k := mapKey.String()
if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
mapKeysSub = append(mapKeysSub, k)
} else {
mapKeysDirect = append(mapKeysDirect, k)
}
}
var writeMapKeys = func(mapKeys []string) {
sort.Strings(mapKeys)
for _, mapKey := range mapKeys {
mrv := rv.MapIndex(reflect.ValueOf(mapKey))
if isNil(mrv) {
// Don't write anything for nil fields.
continue
}
enc.encode(key.add(mapKey), mrv)
}
}
writeMapKeys(mapKeysDirect)
writeMapKeys(mapKeysSub)
}
func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
// Write keys for fields directly under this key first, because if we write
// a field that creates a new table, then all keys under it will be in that
// table (not the one we're writing here).
rt := rv.Type()
var fieldsDirect, fieldsSub [][]int
var addFields func(rt reflect.Type, rv reflect.Value, start []int)
addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
for i := 0; i < rt.NumField(); i++ {
f := rt.Field(i)
// skip unexported fields
if f.PkgPath != "" && !f.Anonymous {
continue
}
frv := rv.Field(i)
if f.Anonymous {
t := f.Type
switch t.Kind() {
case reflect.Struct:
// Treat anonymous struct fields with
// tag names as though they are not
// anonymous, like encoding/json does.
if getOptions(f.Tag).name == "" {
addFields(t, frv, f.Index)
continue
}
case reflect.Ptr:
if t.Elem().Kind() == reflect.Struct &&
getOptions(f.Tag).name == "" {
if !frv.IsNil() {
addFields(t.Elem(), frv.Elem(), f.Index)
}
continue
}
// Fall through to the normal field encoding logic below
// for non-struct anonymous fields.
}
}
if typeIsHash(tomlTypeOfGo(frv)) {
fieldsSub = append(fieldsSub, append(start, f.Index...))
} else {
fieldsDirect = append(fieldsDirect, append(start, f.Index...))
}
}
}
addFields(rt, rv, nil)
var writeFields = func(fields [][]int) {
for _, fieldIndex := range fields {
sft := rt.FieldByIndex(fieldIndex)
sf := rv.FieldByIndex(fieldIndex)
if isNil(sf) {
// Don't write anything for nil fields.
continue
}
opts := getOptions(sft.Tag)
if opts.skip {
continue
}
keyName := sft.Name
if opts.name != "" {
keyName = opts.name
}
if opts.omitempty && isEmpty(sf) {
continue
}
if opts.omitzero && isZero(sf) {
continue
}
enc.encode(key.add(keyName), sf)
}
}
writeFields(fieldsDirect)
writeFields(fieldsSub)
}
// tomlTypeName returns the TOML type name of the Go value's type. It is
// used to determine whether the types of array elements are mixed (which is
// forbidden). If the Go value is nil, then it is illegal for it to be an array
// element, and valueIsNil is returned as true.
// Returns the TOML type of a Go value. The type may be `nil`, which means
// no concrete TOML type could be found.
func tomlTypeOfGo(rv reflect.Value) tomlType {
if isNil(rv) || !rv.IsValid() {
return nil
}
switch rv.Kind() {
case reflect.Bool:
return tomlBool
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64:
return tomlInteger
case reflect.Float32, reflect.Float64:
return tomlFloat
case reflect.Array, reflect.Slice:
if typeEqual(tomlHash, tomlArrayType(rv)) {
return tomlArrayHash
}
return tomlArray
case reflect.Ptr, reflect.Interface:
return tomlTypeOfGo(rv.Elem())
case reflect.String:
return tomlString
case reflect.Map:
return tomlHash
case reflect.Struct:
switch rv.Interface().(type) {
case time.Time:
return tomlDatetime
case TextMarshaler:
return tomlString
default:
return tomlHash
}
default:
panic("unexpected reflect.Kind: " + rv.Kind().String())
}
}
// tomlArrayType returns the element type of a TOML array. The type returned
// may be nil if it cannot be determined (e.g., a nil slice or a zero length
// slize). This function may also panic if it finds a type that cannot be
// expressed in TOML (such as nil elements, heterogeneous arrays or directly
// nested arrays of tables).
func tomlArrayType(rv reflect.Value) tomlType {
if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
return nil
}
firstType := tomlTypeOfGo(rv.Index(0))
if firstType == nil {
encPanic(errArrayNilElement)
}
rvlen := rv.Len()
for i := 1; i < rvlen; i++ {
elem := rv.Index(i)
switch elemType := tomlTypeOfGo(elem); {
case elemType == nil:
encPanic(errArrayNilElement)
case !typeEqual(firstType, elemType):
encPanic(errArrayMixedElementTypes)
}
}
// If we have a nested array, then we must make sure that the nested
// array contains ONLY primitives.
// This checks arbitrarily nested arrays.
if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
nest := tomlArrayType(eindirect(rv.Index(0)))
if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
encPanic(errArrayNoTable)
}
}
return firstType
}
type tagOptions struct {
skip bool // "-"
name string
omitempty bool
omitzero bool
}
func getOptions(tag reflect.StructTag) tagOptions {
t := tag.Get("toml")
if t == "-" {
return tagOptions{skip: true}
}
var opts tagOptions
parts := strings.Split(t, ",")
opts.name = parts[0]
for _, s := range parts[1:] {
switch s {
case "omitempty":
opts.omitempty = true
case "omitzero":
opts.omitzero = true
}
}
return opts
}
func isZero(rv reflect.Value) bool {
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint() == 0
case reflect.Float32, reflect.Float64:
return rv.Float() == 0.0
}
return false
}
func isEmpty(rv reflect.Value) bool {
switch rv.Kind() {
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
return rv.Len() == 0
case reflect.Bool:
return !rv.Bool()
}
return false
}
func (enc *Encoder) newline() {
if enc.hasWritten {
enc.wf("\n")
}
}
func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
if len(key) == 0 {
encPanic(errNoKey)
}
panicIfInvalidKey(key)
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
enc.eElement(val)
enc.newline()
}
func (enc *Encoder) wf(format string, v ...interface{}) {
if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
encPanic(err)
}
enc.hasWritten = true
}
func (enc *Encoder) indentStr(key Key) string {
return strings.Repeat(enc.Indent, len(key)-1)
}
func encPanic(err error) {
panic(tomlEncodeError{err})
}
func eindirect(v reflect.Value) reflect.Value {
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
return eindirect(v.Elem())
default:
return v
}
}
func isNil(rv reflect.Value) bool {
switch rv.Kind() {
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return rv.IsNil()
default:
return false
}
}
func panicIfInvalidKey(key Key) {
for _, k := range key {
if len(k) == 0 {
encPanic(e("Key '%s' is not a valid table name. Key names "+
"cannot be empty.", key.maybeQuotedAll()))
}
}
}
func isValidKeyName(s string) bool {
return len(s) != 0
}

View File

@ -1,19 +0,0 @@
// +build go1.2
package toml
// In order to support Go 1.1, we define our own TextMarshaler and
// TextUnmarshaler types. For Go 1.2+, we just alias them with the
// standard library interfaces.
import (
"encoding"
)
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
// so that Go 1.1 can be supported.
type TextMarshaler encoding.TextMarshaler
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
// here so that Go 1.1 can be supported.
type TextUnmarshaler encoding.TextUnmarshaler

View File

@ -1,18 +0,0 @@
// +build !go1.2
package toml
// These interfaces were introduced in Go 1.2, so we add them manually when
// compiling for Go 1.1.
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
// so that Go 1.1 can be supported.
type TextMarshaler interface {
MarshalText() (text []byte, err error)
}
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
// here so that Go 1.1 can be supported.
type TextUnmarshaler interface {
UnmarshalText(text []byte) error
}

View File

@ -1,953 +0,0 @@
package toml
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
type itemType int
const (
itemError itemType = iota
itemNIL // used in the parser to indicate no type
itemEOF
itemText
itemString
itemRawString
itemMultilineString
itemRawMultilineString
itemBool
itemInteger
itemFloat
itemDatetime
itemArray // the start of an array
itemArrayEnd
itemTableStart
itemTableEnd
itemArrayTableStart
itemArrayTableEnd
itemKeyStart
itemCommentStart
itemInlineTableStart
itemInlineTableEnd
)
const (
eof = 0
comma = ','
tableStart = '['
tableEnd = ']'
arrayTableStart = '['
arrayTableEnd = ']'
tableSep = '.'
keySep = '='
arrayStart = '['
arrayEnd = ']'
commentStart = '#'
stringStart = '"'
stringEnd = '"'
rawStringStart = '\''
rawStringEnd = '\''
inlineTableStart = '{'
inlineTableEnd = '}'
)
type stateFn func(lx *lexer) stateFn
type lexer struct {
input string
start int
pos int
line int
state stateFn
items chan item
// Allow for backing up up to three runes.
// This is necessary because TOML contains 3-rune tokens (""" and ''').
prevWidths [3]int
nprev int // how many of prevWidths are in use
// If we emit an eof, we can still back up, but it is not OK to call
// next again.
atEOF bool
// A stack of state functions used to maintain context.
// The idea is to reuse parts of the state machine in various places.
// For example, values can appear at the top level or within arbitrarily
// nested arrays. The last state on the stack is used after a value has
// been lexed. Similarly for comments.
stack []stateFn
}
type item struct {
typ itemType
val string
line int
}
func (lx *lexer) nextItem() item {
for {
select {
case item := <-lx.items:
return item
default:
lx.state = lx.state(lx)
}
}
}
func lex(input string) *lexer {
lx := &lexer{
input: input,
state: lexTop,
line: 1,
items: make(chan item, 10),
stack: make([]stateFn, 0, 10),
}
return lx
}
func (lx *lexer) push(state stateFn) {
lx.stack = append(lx.stack, state)
}
func (lx *lexer) pop() stateFn {
if len(lx.stack) == 0 {
return lx.errorf("BUG in lexer: no states to pop")
}
last := lx.stack[len(lx.stack)-1]
lx.stack = lx.stack[0 : len(lx.stack)-1]
return last
}
func (lx *lexer) current() string {
return lx.input[lx.start:lx.pos]
}
func (lx *lexer) emit(typ itemType) {
lx.items <- item{typ, lx.current(), lx.line}
lx.start = lx.pos
}
func (lx *lexer) emitTrim(typ itemType) {
lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line}
lx.start = lx.pos
}
func (lx *lexer) next() (r rune) {
if lx.atEOF {
panic("next called after EOF")
}
if lx.pos >= len(lx.input) {
lx.atEOF = true
return eof
}
if lx.input[lx.pos] == '\n' {
lx.line++
}
lx.prevWidths[2] = lx.prevWidths[1]
lx.prevWidths[1] = lx.prevWidths[0]
if lx.nprev < 3 {
lx.nprev++
}
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
lx.prevWidths[0] = w
lx.pos += w
return r
}
// ignore skips over the pending input before this point.
func (lx *lexer) ignore() {
lx.start = lx.pos
}
// backup steps back one rune. Can be called only twice between calls to next.
func (lx *lexer) backup() {
if lx.atEOF {
lx.atEOF = false
return
}
if lx.nprev < 1 {
panic("backed up too far")
}
w := lx.prevWidths[0]
lx.prevWidths[0] = lx.prevWidths[1]
lx.prevWidths[1] = lx.prevWidths[2]
lx.nprev--
lx.pos -= w
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
lx.line--
}
}
// accept consumes the next rune if it's equal to `valid`.
func (lx *lexer) accept(valid rune) bool {
if lx.next() == valid {
return true
}
lx.backup()
return false
}
// peek returns but does not consume the next rune in the input.
func (lx *lexer) peek() rune {
r := lx.next()
lx.backup()
return r
}
// skip ignores all input that matches the given predicate.
func (lx *lexer) skip(pred func(rune) bool) {
for {
r := lx.next()
if pred(r) {
continue
}
lx.backup()
lx.ignore()
return
}
}
// errorf stops all lexing by emitting an error and returning `nil`.
// Note that any value that is a character is escaped if it's a special
// character (newlines, tabs, etc.).
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
lx.items <- item{
itemError,
fmt.Sprintf(format, values...),
lx.line,
}
return nil
}
// lexTop consumes elements at the top level of TOML data.
func lexTop(lx *lexer) stateFn {
r := lx.next()
if isWhitespace(r) || isNL(r) {
return lexSkip(lx, lexTop)
}
switch r {
case commentStart:
lx.push(lexTop)
return lexCommentStart
case tableStart:
return lexTableStart
case eof:
if lx.pos > lx.start {
return lx.errorf("unexpected EOF")
}
lx.emit(itemEOF)
return nil
}
// At this point, the only valid item can be a key, so we back up
// and let the key lexer do the rest.
lx.backup()
lx.push(lexTopEnd)
return lexKeyStart
}
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
// or a table.) It must see only whitespace, and will turn back to lexTop
// upon a newline. If it sees EOF, it will quit the lexer successfully.
func lexTopEnd(lx *lexer) stateFn {
r := lx.next()
switch {
case r == commentStart:
// a comment will read to a newline for us.
lx.push(lexTop)
return lexCommentStart
case isWhitespace(r):
return lexTopEnd
case isNL(r):
lx.ignore()
return lexTop
case r == eof:
lx.emit(itemEOF)
return nil
}
return lx.errorf("expected a top-level item to end with a newline, "+
"comment, or EOF, but got %q instead", r)
}
// lexTable lexes the beginning of a table. Namely, it makes sure that
// it starts with a character other than '.' and ']'.
// It assumes that '[' has already been consumed.
// It also handles the case that this is an item in an array of tables.
// e.g., '[[name]]'.
func lexTableStart(lx *lexer) stateFn {
if lx.peek() == arrayTableStart {
lx.next()
lx.emit(itemArrayTableStart)
lx.push(lexArrayTableEnd)
} else {
lx.emit(itemTableStart)
lx.push(lexTableEnd)
}
return lexTableNameStart
}
func lexTableEnd(lx *lexer) stateFn {
lx.emit(itemTableEnd)
return lexTopEnd
}
func lexArrayTableEnd(lx *lexer) stateFn {
if r := lx.next(); r != arrayTableEnd {
return lx.errorf("expected end of table array name delimiter %q, "+
"but got %q instead", arrayTableEnd, r)
}
lx.emit(itemArrayTableEnd)
return lexTopEnd
}
func lexTableNameStart(lx *lexer) stateFn {
lx.skip(isWhitespace)
switch r := lx.peek(); {
case r == tableEnd || r == eof:
return lx.errorf("unexpected end of table name " +
"(table names cannot be empty)")
case r == tableSep:
return lx.errorf("unexpected table separator " +
"(table names cannot be empty)")
case r == stringStart || r == rawStringStart:
lx.ignore()
lx.push(lexTableNameEnd)
return lexValue // reuse string lexing
default:
return lexBareTableName
}
}
// lexBareTableName lexes the name of a table. It assumes that at least one
// valid character for the table has already been read.
func lexBareTableName(lx *lexer) stateFn {
r := lx.next()
if isBareKeyChar(r) {
return lexBareTableName
}
lx.backup()
lx.emit(itemText)
return lexTableNameEnd
}
// lexTableNameEnd reads the end of a piece of a table name, optionally
// consuming whitespace.
func lexTableNameEnd(lx *lexer) stateFn {
lx.skip(isWhitespace)
switch r := lx.next(); {
case isWhitespace(r):
return lexTableNameEnd
case r == tableSep:
lx.ignore()
return lexTableNameStart
case r == tableEnd:
return lx.pop()
default:
return lx.errorf("expected '.' or ']' to end table name, "+
"but got %q instead", r)
}
}
// lexKeyStart consumes a key name up until the first non-whitespace character.
// lexKeyStart will ignore whitespace.
func lexKeyStart(lx *lexer) stateFn {
r := lx.peek()
switch {
case r == keySep:
return lx.errorf("unexpected key separator %q", keySep)
case isWhitespace(r) || isNL(r):
lx.next()
return lexSkip(lx, lexKeyStart)
case r == stringStart || r == rawStringStart:
lx.ignore()
lx.emit(itemKeyStart)
lx.push(lexKeyEnd)
return lexValue // reuse string lexing
default:
lx.ignore()
lx.emit(itemKeyStart)
return lexBareKey
}
}
// lexBareKey consumes the text of a bare key. Assumes that the first character
// (which is not whitespace) has not yet been consumed.
func lexBareKey(lx *lexer) stateFn {
switch r := lx.next(); {
case isBareKeyChar(r):
return lexBareKey
case isWhitespace(r):
lx.backup()
lx.emit(itemText)
return lexKeyEnd
case r == keySep:
lx.backup()
lx.emit(itemText)
return lexKeyEnd
default:
return lx.errorf("bare keys cannot contain %q", r)
}
}
// lexKeyEnd consumes the end of a key and trims whitespace (up to the key
// separator).
func lexKeyEnd(lx *lexer) stateFn {
switch r := lx.next(); {
case r == keySep:
return lexSkip(lx, lexValue)
case isWhitespace(r):
return lexSkip(lx, lexKeyEnd)
default:
return lx.errorf("expected key separator %q, but got %q instead",
keySep, r)
}
}
// lexValue starts the consumption of a value anywhere a value is expected.
// lexValue will ignore whitespace.
// After a value is lexed, the last state on the next is popped and returned.
func lexValue(lx *lexer) stateFn {
// We allow whitespace to precede a value, but NOT newlines.
// In array syntax, the array states are responsible for ignoring newlines.
r := lx.next()
switch {
case isWhitespace(r):
return lexSkip(lx, lexValue)
case isDigit(r):
lx.backup() // avoid an extra state and use the same as above
return lexNumberOrDateStart
}
switch r {
case arrayStart:
lx.ignore()
lx.emit(itemArray)
return lexArrayValue
case inlineTableStart:
lx.ignore()
lx.emit(itemInlineTableStart)
return lexInlineTableValue
case stringStart:
if lx.accept(stringStart) {
if lx.accept(stringStart) {
lx.ignore() // Ignore """
return lexMultilineString
}
lx.backup()
}
lx.ignore() // ignore the '"'
return lexString
case rawStringStart:
if lx.accept(rawStringStart) {
if lx.accept(rawStringStart) {
lx.ignore() // Ignore """
return lexMultilineRawString
}
lx.backup()
}
lx.ignore() // ignore the "'"
return lexRawString
case '+', '-':
return lexNumberStart
case '.': // special error case, be kind to users
return lx.errorf("floats must start with a digit, not '.'")
}
if unicode.IsLetter(r) {
// Be permissive here; lexBool will give a nice error if the
// user wrote something like
// x = foo
// (i.e. not 'true' or 'false' but is something else word-like.)
lx.backup()
return lexBool
}
return lx.errorf("expected value but found %q instead", r)
}
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
// have already been consumed. All whitespace and newlines are ignored.
func lexArrayValue(lx *lexer) stateFn {
r := lx.next()
switch {
case isWhitespace(r) || isNL(r):
return lexSkip(lx, lexArrayValue)
case r == commentStart:
lx.push(lexArrayValue)
return lexCommentStart
case r == comma:
return lx.errorf("unexpected comma")
case r == arrayEnd:
// NOTE(caleb): The spec isn't clear about whether you can have
// a trailing comma or not, so we'll allow it.
return lexArrayEnd
}
lx.backup()
lx.push(lexArrayValueEnd)
return lexValue
}
// lexArrayValueEnd consumes everything between the end of an array value and
// the next value (or the end of the array): it ignores whitespace and newlines
// and expects either a ',' or a ']'.
func lexArrayValueEnd(lx *lexer) stateFn {
r := lx.next()
switch {
case isWhitespace(r) || isNL(r):
return lexSkip(lx, lexArrayValueEnd)
case r == commentStart:
lx.push(lexArrayValueEnd)
return lexCommentStart
case r == comma:
lx.ignore()
return lexArrayValue // move on to the next value
case r == arrayEnd:
return lexArrayEnd
}
return lx.errorf(
"expected a comma or array terminator %q, but got %q instead",
arrayEnd, r,
)
}
// lexArrayEnd finishes the lexing of an array.
// It assumes that a ']' has just been consumed.
func lexArrayEnd(lx *lexer) stateFn {
lx.ignore()
lx.emit(itemArrayEnd)
return lx.pop()
}
// lexInlineTableValue consumes one key/value pair in an inline table.
// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
func lexInlineTableValue(lx *lexer) stateFn {
r := lx.next()
switch {
case isWhitespace(r):
return lexSkip(lx, lexInlineTableValue)
case isNL(r):
return lx.errorf("newlines not allowed within inline tables")
case r == commentStart:
lx.push(lexInlineTableValue)
return lexCommentStart
case r == comma:
return lx.errorf("unexpected comma")
case r == inlineTableEnd:
return lexInlineTableEnd
}
lx.backup()
lx.push(lexInlineTableValueEnd)
return lexKeyStart
}
// lexInlineTableValueEnd consumes everything between the end of an inline table
// key/value pair and the next pair (or the end of the table):
// it ignores whitespace and expects either a ',' or a '}'.
func lexInlineTableValueEnd(lx *lexer) stateFn {
r := lx.next()
switch {
case isWhitespace(r):
return lexSkip(lx, lexInlineTableValueEnd)
case isNL(r):
return lx.errorf("newlines not allowed within inline tables")
case r == commentStart:
lx.push(lexInlineTableValueEnd)
return lexCommentStart
case r == comma:
lx.ignore()
return lexInlineTableValue
case r == inlineTableEnd:
return lexInlineTableEnd
}
return lx.errorf("expected a comma or an inline table terminator %q, "+
"but got %q instead", inlineTableEnd, r)
}
// lexInlineTableEnd finishes the lexing of an inline table.
// It assumes that a '}' has just been consumed.
func lexInlineTableEnd(lx *lexer) stateFn {
lx.ignore()
lx.emit(itemInlineTableEnd)
return lx.pop()
}
// lexString consumes the inner contents of a string. It assumes that the
// beginning '"' has already been consumed and ignored.
func lexString(lx *lexer) stateFn {
r := lx.next()
switch {
case r == eof:
return lx.errorf("unexpected EOF")
case isNL(r):
return lx.errorf("strings cannot contain newlines")
case r == '\\':
lx.push(lexString)
return lexStringEscape
case r == stringEnd:
lx.backup()
lx.emit(itemString)
lx.next()
lx.ignore()
return lx.pop()
}
return lexString
}
// lexMultilineString consumes the inner contents of a string. It assumes that
// the beginning '"""' has already been consumed and ignored.
func lexMultilineString(lx *lexer) stateFn {
switch lx.next() {
case eof:
return lx.errorf("unexpected EOF")
case '\\':
return lexMultilineStringEscape
case stringEnd:
if lx.accept(stringEnd) {
if lx.accept(stringEnd) {
lx.backup()
lx.backup()
lx.backup()
lx.emit(itemMultilineString)
lx.next()
lx.next()
lx.next()
lx.ignore()
return lx.pop()
}
lx.backup()
}
}
return lexMultilineString
}
// lexRawString consumes a raw string. Nothing can be escaped in such a string.
// It assumes that the beginning "'" has already been consumed and ignored.
func lexRawString(lx *lexer) stateFn {
r := lx.next()
switch {
case r == eof:
return lx.errorf("unexpected EOF")
case isNL(r):
return lx.errorf("strings cannot contain newlines")
case r == rawStringEnd:
lx.backup()
lx.emit(itemRawString)
lx.next()
lx.ignore()
return lx.pop()
}
return lexRawString
}
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
// a string. It assumes that the beginning "'''" has already been consumed and
// ignored.
func lexMultilineRawString(lx *lexer) stateFn {
switch lx.next() {
case eof:
return lx.errorf("unexpected EOF")
case rawStringEnd:
if lx.accept(rawStringEnd) {
if lx.accept(rawStringEnd) {
lx.backup()
lx.backup()
lx.backup()
lx.emit(itemRawMultilineString)
lx.next()
lx.next()
lx.next()
lx.ignore()
return lx.pop()
}
lx.backup()
}
}
return lexMultilineRawString
}
// lexMultilineStringEscape consumes an escaped character. It assumes that the
// preceding '\\' has already been consumed.
func lexMultilineStringEscape(lx *lexer) stateFn {
// Handle the special case first:
if isNL(lx.next()) {
return lexMultilineString
}
lx.backup()
lx.push(lexMultilineString)
return lexStringEscape(lx)
}
func lexStringEscape(lx *lexer) stateFn {
r := lx.next()
switch r {
case 'b':
fallthrough
case 't':
fallthrough
case 'n':
fallthrough
case 'f':
fallthrough
case 'r':
fallthrough
case '"':
fallthrough
case '\\':
return lx.pop()
case 'u':
return lexShortUnicodeEscape
case 'U':
return lexLongUnicodeEscape
}
return lx.errorf("invalid escape character %q; only the following "+
"escape characters are allowed: "+
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
}
func lexShortUnicodeEscape(lx *lexer) stateFn {
var r rune
for i := 0; i < 4; i++ {
r = lx.next()
if !isHexadecimal(r) {
return lx.errorf(`expected four hexadecimal digits after '\u', `+
"but got %q instead", lx.current())
}
}
return lx.pop()
}
func lexLongUnicodeEscape(lx *lexer) stateFn {
var r rune
for i := 0; i < 8; i++ {
r = lx.next()
if !isHexadecimal(r) {
return lx.errorf(`expected eight hexadecimal digits after '\U', `+
"but got %q instead", lx.current())
}
}
return lx.pop()
}
// lexNumberOrDateStart consumes either an integer, a float, or datetime.
func lexNumberOrDateStart(lx *lexer) stateFn {
r := lx.next()
if isDigit(r) {
return lexNumberOrDate
}
switch r {
case '_':
return lexNumber
case 'e', 'E':
return lexFloat
case '.':
return lx.errorf("floats must start with a digit, not '.'")
}
return lx.errorf("expected a digit but got %q", r)
}
// lexNumberOrDate consumes either an integer, float or datetime.
func lexNumberOrDate(lx *lexer) stateFn {
r := lx.next()
if isDigit(r) {
return lexNumberOrDate
}
switch r {
case '-':
return lexDatetime
case '_':
return lexNumber
case '.', 'e', 'E':
return lexFloat
}
lx.backup()
lx.emit(itemInteger)
return lx.pop()
}
// lexDatetime consumes a Datetime, to a first approximation.
// The parser validates that it matches one of the accepted formats.
func lexDatetime(lx *lexer) stateFn {
r := lx.next()
if isDigit(r) {
return lexDatetime
}
switch r {
case '-', 'T', ':', '.', 'Z', '+':
return lexDatetime
}
lx.backup()
lx.emit(itemDatetime)
return lx.pop()
}
// lexNumberStart consumes either an integer or a float. It assumes that a sign
// has already been read, but that *no* digits have been consumed.
// lexNumberStart will move to the appropriate integer or float states.
func lexNumberStart(lx *lexer) stateFn {
// We MUST see a digit. Even floats have to start with a digit.
r := lx.next()
if !isDigit(r) {
if r == '.' {
return lx.errorf("floats must start with a digit, not '.'")
}
return lx.errorf("expected a digit but got %q", r)
}
return lexNumber
}
// lexNumber consumes an integer or a float after seeing the first digit.
func lexNumber(lx *lexer) stateFn {
r := lx.next()
if isDigit(r) {
return lexNumber
}
switch r {
case '_':
return lexNumber
case '.', 'e', 'E':
return lexFloat
}
lx.backup()
lx.emit(itemInteger)
return lx.pop()
}
// lexFloat consumes the elements of a float. It allows any sequence of
// float-like characters, so floats emitted by the lexer are only a first
// approximation and must be validated by the parser.
func lexFloat(lx *lexer) stateFn {
r := lx.next()
if isDigit(r) {
return lexFloat
}
switch r {
case '_', '.', '-', '+', 'e', 'E':
return lexFloat
}
lx.backup()
lx.emit(itemFloat)
return lx.pop()
}
// lexBool consumes a bool string: 'true' or 'false.
func lexBool(lx *lexer) stateFn {
var rs []rune
for {
r := lx.next()
if !unicode.IsLetter(r) {
lx.backup()
break
}
rs = append(rs, r)
}
s := string(rs)
switch s {
case "true", "false":
lx.emit(itemBool)
return lx.pop()
}
return lx.errorf("expected value but found %q instead", s)
}
// lexCommentStart begins the lexing of a comment. It will emit
// itemCommentStart and consume no characters, passing control to lexComment.
func lexCommentStart(lx *lexer) stateFn {
lx.ignore()
lx.emit(itemCommentStart)
return lexComment
}
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
// It will consume *up to* the first newline character, and pass control
// back to the last state on the stack.
func lexComment(lx *lexer) stateFn {
r := lx.peek()
if isNL(r) || r == eof {
lx.emit(itemText)
return lx.pop()
}
lx.next()
return lexComment
}
// lexSkip ignores all slurped input and moves on to the next state.
func lexSkip(lx *lexer, nextState stateFn) stateFn {
return func(lx *lexer) stateFn {
lx.ignore()
return nextState
}
}
// isWhitespace returns true if `r` is a whitespace character according
// to the spec.
func isWhitespace(r rune) bool {
return r == '\t' || r == ' '
}
func isNL(r rune) bool {
return r == '\n' || r == '\r'
}
func isDigit(r rune) bool {
return r >= '0' && r <= '9'
}
func isHexadecimal(r rune) bool {
return (r >= '0' && r <= '9') ||
(r >= 'a' && r <= 'f') ||
(r >= 'A' && r <= 'F')
}
func isBareKeyChar(r rune) bool {
return (r >= 'A' && r <= 'Z') ||
(r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') ||
r == '_' ||
r == '-'
}
func (itype itemType) String() string {
switch itype {
case itemError:
return "Error"
case itemNIL:
return "NIL"
case itemEOF:
return "EOF"
case itemText:
return "Text"
case itemString, itemRawString, itemMultilineString, itemRawMultilineString:
return "String"
case itemBool:
return "Bool"
case itemInteger:
return "Integer"
case itemFloat:
return "Float"
case itemDatetime:
return "DateTime"
case itemTableStart:
return "TableStart"
case itemTableEnd:
return "TableEnd"
case itemKeyStart:
return "KeyStart"
case itemArray:
return "Array"
case itemArrayEnd:
return "ArrayEnd"
case itemCommentStart:
return "CommentStart"
}
panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype)))
}
func (item item) String() string {
return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val)
}

View File

@ -1,592 +0,0 @@
package toml
import (
"fmt"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
)
type parser struct {
mapping map[string]interface{}
types map[string]tomlType
lx *lexer
// A list of keys in the order that they appear in the TOML data.
ordered []Key
// the full key for the current hash in scope
context Key
// the base key name for everything except hashes
currentKey string
// rough approximation of line number
approxLine int
// A map of 'key.group.names' to whether they were created implicitly.
implicits map[string]bool
}
type parseError string
func (pe parseError) Error() string {
return string(pe)
}
func parse(data string) (p *parser, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
if err, ok = r.(parseError); ok {
return
}
panic(r)
}
}()
p = &parser{
mapping: make(map[string]interface{}),
types: make(map[string]tomlType),
lx: lex(data),
ordered: make([]Key, 0),
implicits: make(map[string]bool),
}
for {
item := p.next()
if item.typ == itemEOF {
break
}
p.topLevel(item)
}
return p, nil
}
func (p *parser) panicf(format string, v ...interface{}) {
msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
p.approxLine, p.current(), fmt.Sprintf(format, v...))
panic(parseError(msg))
}
func (p *parser) next() item {
it := p.lx.nextItem()
if it.typ == itemError {
p.panicf("%s", it.val)
}
return it
}
func (p *parser) bug(format string, v ...interface{}) {
panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
}
func (p *parser) expect(typ itemType) item {
it := p.next()
p.assertEqual(typ, it.typ)
return it
}
func (p *parser) assertEqual(expected, got itemType) {
if expected != got {
p.bug("Expected '%s' but got '%s'.", expected, got)
}
}
func (p *parser) topLevel(item item) {
switch item.typ {
case itemCommentStart:
p.approxLine = item.line
p.expect(itemText)
case itemTableStart:
kg := p.next()
p.approxLine = kg.line
var key Key
for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
key = append(key, p.keyString(kg))
}
p.assertEqual(itemTableEnd, kg.typ)
p.establishContext(key, false)
p.setType("", tomlHash)
p.ordered = append(p.ordered, key)
case itemArrayTableStart:
kg := p.next()
p.approxLine = kg.line
var key Key
for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
key = append(key, p.keyString(kg))
}
p.assertEqual(itemArrayTableEnd, kg.typ)
p.establishContext(key, true)
p.setType("", tomlArrayHash)
p.ordered = append(p.ordered, key)
case itemKeyStart:
kname := p.next()
p.approxLine = kname.line
p.currentKey = p.keyString(kname)
val, typ := p.value(p.next())
p.setValue(p.currentKey, val)
p.setType(p.currentKey, typ)
p.ordered = append(p.ordered, p.context.add(p.currentKey))
p.currentKey = ""
default:
p.bug("Unexpected type at top level: %s", item.typ)
}
}
// Gets a string for a key (or part of a key in a table name).
func (p *parser) keyString(it item) string {
switch it.typ {
case itemText:
return it.val
case itemString, itemMultilineString,
itemRawString, itemRawMultilineString:
s, _ := p.value(it)
return s.(string)
default:
p.bug("Unexpected key type: %s", it.typ)
panic("unreachable")
}
}
// value translates an expected value from the lexer into a Go value wrapped
// as an empty interface.
func (p *parser) value(it item) (interface{}, tomlType) {
switch it.typ {
case itemString:
return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
case itemMultilineString:
trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
case itemRawString:
return it.val, p.typeOfPrimitive(it)
case itemRawMultilineString:
return stripFirstNewline(it.val), p.typeOfPrimitive(it)
case itemBool:
switch it.val {
case "true":
return true, p.typeOfPrimitive(it)
case "false":
return false, p.typeOfPrimitive(it)
}
p.bug("Expected boolean value, but got '%s'.", it.val)
case itemInteger:
if !numUnderscoresOK(it.val) {
p.panicf("Invalid integer %q: underscores must be surrounded by digits",
it.val)
}
val := strings.Replace(it.val, "_", "", -1)
num, err := strconv.ParseInt(val, 10, 64)
if err != nil {
// Distinguish integer values. Normally, it'd be a bug if the lexer
// provides an invalid integer, but it's possible that the number is
// out of range of valid values (which the lexer cannot determine).
// So mark the former as a bug but the latter as a legitimate user
// error.
if e, ok := err.(*strconv.NumError); ok &&
e.Err == strconv.ErrRange {
p.panicf("Integer '%s' is out of the range of 64-bit "+
"signed integers.", it.val)
} else {
p.bug("Expected integer value, but got '%s'.", it.val)
}
}
return num, p.typeOfPrimitive(it)
case itemFloat:
parts := strings.FieldsFunc(it.val, func(r rune) bool {
switch r {
case '.', 'e', 'E':
return true
}
return false
})
for _, part := range parts {
if !numUnderscoresOK(part) {
p.panicf("Invalid float %q: underscores must be "+
"surrounded by digits", it.val)
}
}
if !numPeriodsOK(it.val) {
// As a special case, numbers like '123.' or '1.e2',
// which are valid as far as Go/strconv are concerned,
// must be rejected because TOML says that a fractional
// part consists of '.' followed by 1+ digits.
p.panicf("Invalid float %q: '.' must be followed "+
"by one or more digits", it.val)
}
val := strings.Replace(it.val, "_", "", -1)
num, err := strconv.ParseFloat(val, 64)
if err != nil {
if e, ok := err.(*strconv.NumError); ok &&
e.Err == strconv.ErrRange {
p.panicf("Float '%s' is out of the range of 64-bit "+
"IEEE-754 floating-point numbers.", it.val)
} else {
p.panicf("Invalid float value: %q", it.val)
}
}
return num, p.typeOfPrimitive(it)
case itemDatetime:
var t time.Time
var ok bool
var err error
for _, format := range []string{
"2006-01-02T15:04:05Z07:00",
"2006-01-02T15:04:05",
"2006-01-02",
} {
t, err = time.ParseInLocation(format, it.val, time.Local)
if err == nil {
ok = true
break
}
}
if !ok {
p.panicf("Invalid TOML Datetime: %q.", it.val)
}
return t, p.typeOfPrimitive(it)
case itemArray:
array := make([]interface{}, 0)
types := make([]tomlType, 0)
for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
if it.typ == itemCommentStart {
p.expect(itemText)
continue
}
val, typ := p.value(it)
array = append(array, val)
types = append(types, typ)
}
return array, p.typeOfArray(types)
case itemInlineTableStart:
var (
hash = make(map[string]interface{})
outerContext = p.context
outerKey = p.currentKey
)
p.context = append(p.context, p.currentKey)
p.currentKey = ""
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
if it.typ != itemKeyStart {
p.bug("Expected key start but instead found %q, around line %d",
it.val, p.approxLine)
}
if it.typ == itemCommentStart {
p.expect(itemText)
continue
}
// retrieve key
k := p.next()
p.approxLine = k.line
kname := p.keyString(k)
// retrieve value
p.currentKey = kname
val, typ := p.value(p.next())
// make sure we keep metadata up to date
p.setType(kname, typ)
p.ordered = append(p.ordered, p.context.add(p.currentKey))
hash[kname] = val
}
p.context = outerContext
p.currentKey = outerKey
return hash, tomlHash
}
p.bug("Unexpected value type: %s", it.typ)
panic("unreachable")
}
// numUnderscoresOK checks whether each underscore in s is surrounded by
// characters that are not underscores.
func numUnderscoresOK(s string) bool {
accept := false
for _, r := range s {
if r == '_' {
if !accept {
return false
}
accept = false
continue
}
accept = true
}
return accept
}
// numPeriodsOK checks whether every period in s is followed by a digit.
func numPeriodsOK(s string) bool {
period := false
for _, r := range s {
if period && !isDigit(r) {
return false
}
period = r == '.'
}
return !period
}
// establishContext sets the current context of the parser,
// where the context is either a hash or an array of hashes. Which one is
// set depends on the value of the `array` parameter.
//
// Establishing the context also makes sure that the key isn't a duplicate, and
// will create implicit hashes automatically.
func (p *parser) establishContext(key Key, array bool) {
var ok bool
// Always start at the top level and drill down for our context.
hashContext := p.mapping
keyContext := make(Key, 0)
// We only need implicit hashes for key[0:-1]
for _, k := range key[0 : len(key)-1] {
_, ok = hashContext[k]
keyContext = append(keyContext, k)
// No key? Make an implicit hash and move on.
if !ok {
p.addImplicit(keyContext)
hashContext[k] = make(map[string]interface{})
}
// If the hash context is actually an array of tables, then set
// the hash context to the last element in that array.
//
// Otherwise, it better be a table, since this MUST be a key group (by
// virtue of it not being the last element in a key).
switch t := hashContext[k].(type) {
case []map[string]interface{}:
hashContext = t[len(t)-1]
case map[string]interface{}:
hashContext = t
default:
p.panicf("Key '%s' was already created as a hash.", keyContext)
}
}
p.context = keyContext
if array {
// If this is the first element for this array, then allocate a new
// list of tables for it.
k := key[len(key)-1]
if _, ok := hashContext[k]; !ok {
hashContext[k] = make([]map[string]interface{}, 0, 5)
}
// Add a new table. But make sure the key hasn't already been used
// for something else.
if hash, ok := hashContext[k].([]map[string]interface{}); ok {
hashContext[k] = append(hash, make(map[string]interface{}))
} else {
p.panicf("Key '%s' was already created and cannot be used as "+
"an array.", keyContext)
}
} else {
p.setValue(key[len(key)-1], make(map[string]interface{}))
}
p.context = append(p.context, key[len(key)-1])
}
// setValue sets the given key to the given value in the current context.
// It will make sure that the key hasn't already been defined, account for
// implicit key groups.
func (p *parser) setValue(key string, value interface{}) {
var tmpHash interface{}
var ok bool
hash := p.mapping
keyContext := make(Key, 0)
for _, k := range p.context {
keyContext = append(keyContext, k)
if tmpHash, ok = hash[k]; !ok {
p.bug("Context for key '%s' has not been established.", keyContext)
}
switch t := tmpHash.(type) {
case []map[string]interface{}:
// The context is a table of hashes. Pick the most recent table
// defined as the current hash.
hash = t[len(t)-1]
case map[string]interface{}:
hash = t
default:
p.bug("Expected hash to have type 'map[string]interface{}', but "+
"it has '%T' instead.", tmpHash)
}
}
keyContext = append(keyContext, key)
if _, ok := hash[key]; ok {
// Typically, if the given key has already been set, then we have
// to raise an error since duplicate keys are disallowed. However,
// it's possible that a key was previously defined implicitly. In this
// case, it is allowed to be redefined concretely. (See the
// `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
//
// But we have to make sure to stop marking it as an implicit. (So that
// another redefinition provokes an error.)
//
// Note that since it has already been defined (as a hash), we don't
// want to overwrite it. So our business is done.
if p.isImplicit(keyContext) {
p.removeImplicit(keyContext)
return
}
// Otherwise, we have a concrete key trying to override a previous
// key, which is *always* wrong.
p.panicf("Key '%s' has already been defined.", keyContext)
}
hash[key] = value
}
// setType sets the type of a particular value at a given key.
// It should be called immediately AFTER setValue.
//
// Note that if `key` is empty, then the type given will be applied to the
// current context (which is either a table or an array of tables).
func (p *parser) setType(key string, typ tomlType) {
keyContext := make(Key, 0, len(p.context)+1)
for _, k := range p.context {
keyContext = append(keyContext, k)
}
if len(key) > 0 { // allow type setting for hashes
keyContext = append(keyContext, key)
}
p.types[keyContext.String()] = typ
}
// addImplicit sets the given Key as having been created implicitly.
func (p *parser) addImplicit(key Key) {
p.implicits[key.String()] = true
}
// removeImplicit stops tagging the given key as having been implicitly
// created.
func (p *parser) removeImplicit(key Key) {
p.implicits[key.String()] = false
}
// isImplicit returns true if the key group pointed to by the key was created
// implicitly.
func (p *parser) isImplicit(key Key) bool {
return p.implicits[key.String()]
}
// current returns the full key name of the current context.
func (p *parser) current() string {
if len(p.currentKey) == 0 {
return p.context.String()
}
if len(p.context) == 0 {
return p.currentKey
}
return fmt.Sprintf("%s.%s", p.context, p.currentKey)
}
func stripFirstNewline(s string) string {
if len(s) == 0 || s[0] != '\n' {
return s
}
return s[1:]
}
func stripEscapedWhitespace(s string) string {
esc := strings.Split(s, "\\\n")
if len(esc) > 1 {
for i := 1; i < len(esc); i++ {
esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
}
}
return strings.Join(esc, "")
}
func (p *parser) replaceEscapes(str string) string {
var replaced []rune
s := []byte(str)
r := 0
for r < len(s) {
if s[r] != '\\' {
c, size := utf8.DecodeRune(s[r:])
r += size
replaced = append(replaced, c)
continue
}
r += 1
if r >= len(s) {
p.bug("Escape sequence at end of string.")
return ""
}
switch s[r] {
default:
p.bug("Expected valid escape code after \\, but got %q.", s[r])
return ""
case 'b':
replaced = append(replaced, rune(0x0008))
r += 1
case 't':
replaced = append(replaced, rune(0x0009))
r += 1
case 'n':
replaced = append(replaced, rune(0x000A))
r += 1
case 'f':
replaced = append(replaced, rune(0x000C))
r += 1
case 'r':
replaced = append(replaced, rune(0x000D))
r += 1
case '"':
replaced = append(replaced, rune(0x0022))
r += 1
case '\\':
replaced = append(replaced, rune(0x005C))
r += 1
case 'u':
// At this point, we know we have a Unicode escape of the form
// `uXXXX` at [r, r+5). (Because the lexer guarantees this
// for us.)
escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
replaced = append(replaced, escaped)
r += 5
case 'U':
// At this point, we know we have a Unicode escape of the form
// `uXXXX` at [r, r+9). (Because the lexer guarantees this
// for us.)
escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
replaced = append(replaced, escaped)
r += 9
}
}
return string(replaced)
}
func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
s := string(bs)
hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
if err != nil {
p.bug("Could not parse '%s' as a hexadecimal number, but the "+
"lexer claims it's OK: %s", s, err)
}
if !utf8.ValidRune(rune(hex)) {
p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
}
return rune(hex)
}
func isStringType(ty itemType) bool {
return ty == itemString || ty == itemMultilineString ||
ty == itemRawString || ty == itemRawMultilineString
}

View File

@ -1 +0,0 @@
au BufWritePost *.go silent!make tags > /dev/null 2>&1

View File

@ -1,91 +0,0 @@
package toml
// tomlType represents any Go type that corresponds to a TOML type.
// While the first draft of the TOML spec has a simplistic type system that
// probably doesn't need this level of sophistication, we seem to be militating
// toward adding real composite types.
type tomlType interface {
typeString() string
}
// typeEqual accepts any two types and returns true if they are equal.
func typeEqual(t1, t2 tomlType) bool {
if t1 == nil || t2 == nil {
return false
}
return t1.typeString() == t2.typeString()
}
func typeIsHash(t tomlType) bool {
return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
}
type tomlBaseType string
func (btype tomlBaseType) typeString() string {
return string(btype)
}
func (btype tomlBaseType) String() string {
return btype.typeString()
}
var (
tomlInteger tomlBaseType = "Integer"
tomlFloat tomlBaseType = "Float"
tomlDatetime tomlBaseType = "Datetime"
tomlString tomlBaseType = "String"
tomlBool tomlBaseType = "Bool"
tomlArray tomlBaseType = "Array"
tomlHash tomlBaseType = "Hash"
tomlArrayHash tomlBaseType = "ArrayHash"
)
// typeOfPrimitive returns a tomlType of any primitive value in TOML.
// Primitive values are: Integer, Float, Datetime, String and Bool.
//
// Passing a lexer item other than the following will cause a BUG message
// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
func (p *parser) typeOfPrimitive(lexItem item) tomlType {
switch lexItem.typ {
case itemInteger:
return tomlInteger
case itemFloat:
return tomlFloat
case itemDatetime:
return tomlDatetime
case itemString:
return tomlString
case itemMultilineString:
return tomlString
case itemRawString:
return tomlString
case itemRawMultilineString:
return tomlString
case itemBool:
return tomlBool
}
p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
panic("unreachable")
}
// typeOfArray returns a tomlType for an array given a list of types of its
// values.
//
// In the current spec, if an array is homogeneous, then its type is always
// "Array". If the array is not homogeneous, an error is generated.
func (p *parser) typeOfArray(types []tomlType) tomlType {
// Empty arrays are cool.
if len(types) == 0 {
return tomlArray
}
theType := types[0]
for _, t := range types[1:] {
if !typeEqual(theType, t) {
p.panicf("Array contains values of type '%s' and '%s', but "+
"arrays must be homogeneous.", theType, t)
}
}
return tomlArray
}

View File

@ -1,242 +0,0 @@
package toml
// Struct field handling is adapted from code in encoding/json:
//
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the Go distribution.
import (
"reflect"
"sort"
"sync"
)
// A field represents a single field found in a struct.
type field struct {
name string // the name of the field (`toml` tag included)
tag bool // whether field has a `toml` tag
index []int // represents the depth of an anonymous field
typ reflect.Type // the type of the field
}
// byName sorts field by name, breaking ties with depth,
// then breaking ties with "name came from toml tag", then
// breaking ties with index sequence.
type byName []field
func (x byName) Len() int { return len(x) }
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byName) Less(i, j int) bool {
if x[i].name != x[j].name {
return x[i].name < x[j].name
}
if len(x[i].index) != len(x[j].index) {
return len(x[i].index) < len(x[j].index)
}
if x[i].tag != x[j].tag {
return x[i].tag
}
return byIndex(x).Less(i, j)
}
// byIndex sorts field by index sequence.
type byIndex []field
func (x byIndex) Len() int { return len(x) }
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byIndex) Less(i, j int) bool {
for k, xik := range x[i].index {
if k >= len(x[j].index) {
return false
}
if xik != x[j].index[k] {
return xik < x[j].index[k]
}
}
return len(x[i].index) < len(x[j].index)
}
// typeFields returns a list of fields that TOML should recognize for the given
// type. The algorithm is breadth-first search over the set of structs to
// include - the top struct and then any reachable anonymous structs.
func typeFields(t reflect.Type) []field {
// Anonymous fields to explore at the current level and the next.
current := []field{}
next := []field{{typ: t}}
// Count of queued names for current level and the next.
count := map[reflect.Type]int{}
nextCount := map[reflect.Type]int{}
// Types already visited at an earlier level.
visited := map[reflect.Type]bool{}
// Fields found.
var fields []field
for len(next) > 0 {
current, next = next, current[:0]
count, nextCount = nextCount, map[reflect.Type]int{}
for _, f := range current {
if visited[f.typ] {
continue
}
visited[f.typ] = true
// Scan f.typ for fields to include.
for i := 0; i < f.typ.NumField(); i++ {
sf := f.typ.Field(i)
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
opts := getOptions(sf.Tag)
if opts.skip {
continue
}
index := make([]int, len(f.index)+1)
copy(index, f.index)
index[len(f.index)] = i
ft := sf.Type
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
// Follow pointer.
ft = ft.Elem()
}
// Record found field and index sequence.
if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
tagged := opts.name != ""
name := opts.name
if name == "" {
name = sf.Name
}
fields = append(fields, field{name, tagged, index, ft})
if count[f.typ] > 1 {
// If there were multiple instances, add a second,
// so that the annihilation code will see a duplicate.
// It only cares about the distinction between 1 or 2,
// so don't bother generating any more copies.
fields = append(fields, fields[len(fields)-1])
}
continue
}
// Record new anonymous struct to explore in next round.
nextCount[ft]++
if nextCount[ft] == 1 {
f := field{name: ft.Name(), index: index, typ: ft}
next = append(next, f)
}
}
}
}
sort.Sort(byName(fields))
// Delete all fields that are hidden by the Go rules for embedded fields,
// except that fields with TOML tags are promoted.
// The fields are sorted in primary order of name, secondary order
// of field index length. Loop over names; for each name, delete
// hidden fields by choosing the one dominant field that survives.
out := fields[:0]
for advance, i := 0, 0; i < len(fields); i += advance {
// One iteration per name.
// Find the sequence of fields with the name of this first field.
fi := fields[i]
name := fi.name
for advance = 1; i+advance < len(fields); advance++ {
fj := fields[i+advance]
if fj.name != name {
break
}
}
if advance == 1 { // Only one field with this name
out = append(out, fi)
continue
}
dominant, ok := dominantField(fields[i : i+advance])
if ok {
out = append(out, dominant)
}
}
fields = out
sort.Sort(byIndex(fields))
return fields
}
// dominantField looks through the fields, all of which are known to
// have the same name, to find the single field that dominates the
// others using Go's embedding rules, modified by the presence of
// TOML tags. If there are multiple top-level fields, the boolean
// will be false: This condition is an error in Go and we skip all
// the fields.
func dominantField(fields []field) (field, bool) {
// The fields are sorted in increasing index-length order. The winner
// must therefore be one with the shortest index length. Drop all
// longer entries, which is easy: just truncate the slice.
length := len(fields[0].index)
tagged := -1 // Index of first tagged field.
for i, f := range fields {
if len(f.index) > length {
fields = fields[:i]
break
}
if f.tag {
if tagged >= 0 {
// Multiple tagged fields at the same level: conflict.
// Return no field.
return field{}, false
}
tagged = i
}
}
if tagged >= 0 {
return fields[tagged], true
}
// All remaining fields have the same length. If there's more than one,
// we have a conflict (two fields named "X" at the same level) and we
// return no field.
if len(fields) > 1 {
return field{}, false
}
return fields[0], true
}
var fieldCache struct {
sync.RWMutex
m map[reflect.Type][]field
}
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
func cachedTypeFields(t reflect.Type) []field {
fieldCache.RLock()
f := fieldCache.m[t]
fieldCache.RUnlock()
if f != nil {
return f
}
// Compute fields without lock.
// Might duplicate effort but won't hold other computations back.
f = typeFields(t)
if f == nil {
f = []field{}
}
fieldCache.Lock()
if fieldCache.m == nil {
fieldCache.m = map[reflect.Type][]field{}
}
fieldCache.m[t] = f
fieldCache.Unlock()
return f
}

View File

@ -1 +0,0 @@
*.exe

View File

@ -1 +0,0 @@
* @microsoft/containerplat

View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,22 +0,0 @@
# go-winio
This repository contains utilities for efficiently performing Win32 IO operations in
Go. Currently, this is focused on accessing named pipes and other file handles, and
for using named pipes as a net transport.
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
newer operating systems. This is similar to the implementation of network sockets in Go's net
package.
Please see the LICENSE file for licensing information.
This project has adopted the [Microsoft Open Source Code of
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
see the [Code of Conduct
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
questions or comments.
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
for another named pipe implementation.

View File

@ -1,280 +0,0 @@
// +build windows
package winio
import (
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"runtime"
"syscall"
"unicode/utf16"
)
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
const (
BackupData = uint32(iota + 1)
BackupEaData
BackupSecurity
BackupAlternateData
BackupLink
BackupPropertyData
BackupObjectId
BackupReparseData
BackupSparseBlock
BackupTxfsData
)
const (
StreamSparseAttributes = uint32(8)
)
const (
WRITE_DAC = 0x40000
WRITE_OWNER = 0x80000
ACCESS_SYSTEM_SECURITY = 0x1000000
)
// BackupHeader represents a backup stream of a file.
type BackupHeader struct {
Id uint32 // The backup stream ID
Attributes uint32 // Stream attributes
Size int64 // The size of the stream in bytes
Name string // The name of the stream (for BackupAlternateData only).
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
}
type win32StreamId struct {
StreamId uint32
Attributes uint32
Size uint64
NameSize uint32
}
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
// of BackupHeader values.
type BackupStreamReader struct {
r io.Reader
bytesLeft int64
}
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
return &BackupStreamReader{r, 0}
}
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
// it was not completely read.
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
if r.bytesLeft > 0 {
if s, ok := r.r.(io.Seeker); ok {
// Make sure Seek on io.SeekCurrent sometimes succeeds
// before trying the actual seek.
if _, err := s.Seek(0, io.SeekCurrent); err == nil {
if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
return nil, err
}
r.bytesLeft = 0
}
}
if _, err := io.Copy(ioutil.Discard, r); err != nil {
return nil, err
}
}
var wsi win32StreamId
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
return nil, err
}
hdr := &BackupHeader{
Id: wsi.StreamId,
Attributes: wsi.Attributes,
Size: int64(wsi.Size),
}
if wsi.NameSize != 0 {
name := make([]uint16, int(wsi.NameSize/2))
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
return nil, err
}
hdr.Name = syscall.UTF16ToString(name)
}
if wsi.StreamId == BackupSparseBlock {
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
return nil, err
}
hdr.Size -= 8
}
r.bytesLeft = hdr.Size
return hdr, nil
}
// Read reads from the current backup stream.
func (r *BackupStreamReader) Read(b []byte) (int, error) {
if r.bytesLeft == 0 {
return 0, io.EOF
}
if int64(len(b)) > r.bytesLeft {
b = b[:r.bytesLeft]
}
n, err := r.r.Read(b)
r.bytesLeft -= int64(n)
if err == io.EOF {
err = io.ErrUnexpectedEOF
} else if r.bytesLeft == 0 && err == nil {
err = io.EOF
}
return n, err
}
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
type BackupStreamWriter struct {
w io.Writer
bytesLeft int64
}
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
return &BackupStreamWriter{w, 0}
}
// WriteHeader writes the next backup stream header and prepares for calls to Write().
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
if w.bytesLeft != 0 {
return fmt.Errorf("missing %d bytes", w.bytesLeft)
}
name := utf16.Encode([]rune(hdr.Name))
wsi := win32StreamId{
StreamId: hdr.Id,
Attributes: hdr.Attributes,
Size: uint64(hdr.Size),
NameSize: uint32(len(name) * 2),
}
if hdr.Id == BackupSparseBlock {
// Include space for the int64 block offset
wsi.Size += 8
}
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
return err
}
if len(name) != 0 {
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
return err
}
}
if hdr.Id == BackupSparseBlock {
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
return err
}
}
w.bytesLeft = hdr.Size
return nil
}
// Write writes to the current backup stream.
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
if w.bytesLeft < int64(len(b)) {
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
}
n, err := w.w.Write(b)
w.bytesLeft -= int64(n)
return n, err
}
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
type BackupFileReader struct {
f *os.File
includeSecurity bool
ctx uintptr
}
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
// Read will attempt to read the security descriptor of the file.
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
r := &BackupFileReader{f, includeSecurity, 0}
return r
}
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
func (r *BackupFileReader) Read(b []byte) (int, error) {
var bytesRead uint32
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
if err != nil {
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
}
runtime.KeepAlive(r.f)
if bytesRead == 0 {
return 0, io.EOF
}
return int(bytesRead), nil
}
// Close frees Win32 resources associated with the BackupFileReader. It does not close
// the underlying file.
func (r *BackupFileReader) Close() error {
if r.ctx != 0 {
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
runtime.KeepAlive(r.f)
r.ctx = 0
}
return nil
}
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
type BackupFileWriter struct {
f *os.File
includeSecurity bool
ctx uintptr
}
// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
// Write() will attempt to restore the security descriptor from the stream.
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
w := &BackupFileWriter{f, includeSecurity, 0}
return w
}
// Write restores a portion of the file using the provided backup stream.
func (w *BackupFileWriter) Write(b []byte) (int, error) {
var bytesWritten uint32
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
if err != nil {
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
}
runtime.KeepAlive(w.f)
if int(bytesWritten) != len(b) {
return int(bytesWritten), errors.New("not all bytes could be written")
}
return len(b), nil
}
// Close frees Win32 resources associated with the BackupFileWriter. It does not
// close the underlying file.
func (w *BackupFileWriter) Close() error {
if w.ctx != 0 {
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
runtime.KeepAlive(w.f)
w.ctx = 0
}
return nil
}
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
// or restore privileges have been acquired.
//
// If the file opened was a directory, it cannot be used with Readdir().
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
winPath, err := syscall.UTF16FromString(path)
if err != nil {
return nil, err
}
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
if err != nil {
err = &os.PathError{Op: "open", Path: path, Err: err}
return nil, err
}
return os.NewFile(uintptr(h), path), nil
}

View File

@ -1,4 +0,0 @@
// +build !windows
// This file only exists to allow go get on non-Windows platforms.
package backuptar

View File

@ -1,68 +0,0 @@
package backuptar
import (
"archive/tar"
"fmt"
"strconv"
"strings"
"time"
)
// Functions copied from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go
// as we need to manage the LIBARCHIVE.creationtime PAXRecord manually.
// Idea taken from containerd which did the same thing.
// parsePAXTime takes a string of the form %d.%d as described in the PAX
// specification. Note that this implementation allows for negative timestamps,
// which is allowed for by the PAX specification, but not always portable.
func parsePAXTime(s string) (time.Time, error) {
const maxNanoSecondDigits = 9
// Split string into seconds and sub-seconds parts.
ss, sn := s, ""
if pos := strings.IndexByte(s, '.'); pos >= 0 {
ss, sn = s[:pos], s[pos+1:]
}
// Parse the seconds.
secs, err := strconv.ParseInt(ss, 10, 64)
if err != nil {
return time.Time{}, tar.ErrHeader
}
if len(sn) == 0 {
return time.Unix(secs, 0), nil // No sub-second values
}
// Parse the nanoseconds.
if strings.Trim(sn, "0123456789") != "" {
return time.Time{}, tar.ErrHeader
}
if len(sn) < maxNanoSecondDigits {
sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
} else {
sn = sn[:maxNanoSecondDigits] // Right truncate
}
nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
if len(ss) > 0 && ss[0] == '-' {
return time.Unix(secs, -1*nsecs), nil // Negative correction
}
return time.Unix(secs, nsecs), nil
}
// formatPAXTime converts ts into a time of the form %d.%d as described in the
// PAX specification. This function is capable of negative timestamps.
func formatPAXTime(ts time.Time) (s string) {
secs, nsecs := ts.Unix(), ts.Nanosecond()
if nsecs == 0 {
return strconv.FormatInt(secs, 10)
}
// If seconds is negative, then perform correction.
sign := ""
if secs < 0 {
sign = "-" // Remember sign
secs = -(secs + 1) // Add a second to secs
nsecs = -(nsecs - 1e9) // Take that second away from nsecs
}
return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0")
}

View File

@ -1,452 +0,0 @@
// +build windows
package backuptar
import (
"archive/tar"
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"github.com/Microsoft/go-winio"
"golang.org/x/sys/windows"
)
const (
c_ISUID = 04000 // Set uid
c_ISGID = 02000 // Set gid
c_ISVTX = 01000 // Save text (sticky bit)
c_ISDIR = 040000 // Directory
c_ISFIFO = 010000 // FIFO
c_ISREG = 0100000 // Regular file
c_ISLNK = 0120000 // Symbolic link
c_ISBLK = 060000 // Block special file
c_ISCHR = 020000 // Character special file
c_ISSOCK = 0140000 // Socket
)
const (
hdrFileAttributes = "MSWINDOWS.fileattr"
hdrSecurityDescriptor = "MSWINDOWS.sd"
hdrRawSecurityDescriptor = "MSWINDOWS.rawsd"
hdrMountPoint = "MSWINDOWS.mountpoint"
hdrEaPrefix = "MSWINDOWS.xattr."
hdrCreationTime = "LIBARCHIVE.creationtime"
)
func writeZeroes(w io.Writer, count int64) error {
buf := make([]byte, 8192)
c := len(buf)
for i := int64(0); i < count; i += int64(c) {
if int64(c) > count-i {
c = int(count - i)
}
_, err := w.Write(buf[:c])
if err != nil {
return err
}
}
return nil
}
func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error {
curOffset := int64(0)
for {
bhdr, err := br.Next()
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
if err != nil {
return err
}
if bhdr.Id != winio.BackupSparseBlock {
return fmt.Errorf("unexpected stream %d", bhdr.Id)
}
// archive/tar does not support writing sparse files
// so just write zeroes to catch up to the current offset.
err = writeZeroes(t, bhdr.Offset-curOffset)
if bhdr.Size == 0 {
break
}
n, err := io.Copy(t, br)
if err != nil {
return err
}
curOffset = bhdr.Offset + n
}
return nil
}
// BasicInfoHeader creates a tar header from basic file information.
func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *tar.Header {
hdr := &tar.Header{
Format: tar.FormatPAX,
Name: filepath.ToSlash(name),
Size: size,
Typeflag: tar.TypeReg,
ModTime: time.Unix(0, fileInfo.LastWriteTime.Nanoseconds()),
ChangeTime: time.Unix(0, fileInfo.ChangeTime.Nanoseconds()),
AccessTime: time.Unix(0, fileInfo.LastAccessTime.Nanoseconds()),
PAXRecords: make(map[string]string),
}
hdr.PAXRecords[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
hdr.PAXRecords[hdrCreationTime] = formatPAXTime(time.Unix(0, fileInfo.CreationTime.Nanoseconds()))
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
hdr.Mode |= c_ISDIR
hdr.Size = 0
hdr.Typeflag = tar.TypeDir
}
return hdr
}
// WriteTarFileFromBackupStream writes a file to a tar writer using data from a Win32 backup stream.
//
// This encodes Win32 metadata as tar pax vendor extensions starting with MSWINDOWS.
//
// The additional Win32 metadata is:
//
// MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value
//
// MSWINDOWS.rawsd: The Win32 security descriptor, in raw binary format
//
// MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink)
func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error {
name = filepath.ToSlash(name)
hdr := BasicInfoHeader(name, size, fileInfo)
// If r can be seeked, then this function is two-pass: pass 1 collects the
// tar header data, and pass 2 copies the data stream. If r cannot be
// seeked, then some header data (in particular EAs) will be silently lost.
var (
restartPos int64
err error
)
sr, readTwice := r.(io.Seeker)
if readTwice {
if restartPos, err = sr.Seek(0, io.SeekCurrent); err != nil {
readTwice = false
}
}
br := winio.NewBackupStreamReader(r)
var dataHdr *winio.BackupHeader
for dataHdr == nil {
bhdr, err := br.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
switch bhdr.Id {
case winio.BackupData:
hdr.Mode |= c_ISREG
if !readTwice {
dataHdr = bhdr
}
case winio.BackupSecurity:
sd, err := ioutil.ReadAll(br)
if err != nil {
return err
}
hdr.PAXRecords[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd)
case winio.BackupReparseData:
hdr.Mode |= c_ISLNK
hdr.Typeflag = tar.TypeSymlink
reparseBuffer, err := ioutil.ReadAll(br)
rp, err := winio.DecodeReparsePoint(reparseBuffer)
if err != nil {
return err
}
if rp.IsMountPoint {
hdr.PAXRecords[hdrMountPoint] = "1"
}
hdr.Linkname = rp.Target
case winio.BackupEaData:
eab, err := ioutil.ReadAll(br)
if err != nil {
return err
}
eas, err := winio.DecodeExtendedAttributes(eab)
if err != nil {
return err
}
for _, ea := range eas {
// Use base64 encoding for the binary value. Note that there
// is no way to encode the EA's flags, since their use doesn't
// make any sense for persisted EAs.
hdr.PAXRecords[hdrEaPrefix+ea.Name] = base64.StdEncoding.EncodeToString(ea.Value)
}
case winio.BackupAlternateData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
// ignore these streams
default:
return fmt.Errorf("%s: unknown stream ID %d", name, bhdr.Id)
}
}
err = t.WriteHeader(hdr)
if err != nil {
return err
}
if readTwice {
// Get back to the data stream.
if _, err = sr.Seek(restartPos, io.SeekStart); err != nil {
return err
}
for dataHdr == nil {
bhdr, err := br.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
if bhdr.Id == winio.BackupData {
dataHdr = bhdr
}
}
}
if dataHdr != nil {
// A data stream was found. Copy the data.
if (dataHdr.Attributes & winio.StreamSparseAttributes) == 0 {
if size != dataHdr.Size {
return fmt.Errorf("%s: mismatch between file size %d and header size %d", name, size, dataHdr.Size)
}
_, err = io.Copy(t, br)
if err != nil {
return err
}
} else {
err = copySparse(t, br)
if err != nil {
return err
}
}
}
// Look for streams after the data stream. The only ones we handle are alternate data streams.
// Other streams may have metadata that could be serialized, but the tar header has already
// been written. In practice, this means that we don't get EA or TXF metadata.
for {
bhdr, err := br.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
switch bhdr.Id {
case winio.BackupAlternateData:
altName := bhdr.Name
if strings.HasSuffix(altName, ":$DATA") {
altName = altName[:len(altName)-len(":$DATA")]
}
if (bhdr.Attributes & winio.StreamSparseAttributes) == 0 {
hdr = &tar.Header{
Format: hdr.Format,
Name: name + altName,
Mode: hdr.Mode,
Typeflag: tar.TypeReg,
Size: bhdr.Size,
ModTime: hdr.ModTime,
AccessTime: hdr.AccessTime,
ChangeTime: hdr.ChangeTime,
}
err = t.WriteHeader(hdr)
if err != nil {
return err
}
_, err = io.Copy(t, br)
if err != nil {
return err
}
} else {
// Unsupported for now, since the size of the alternate stream is not present
// in the backup stream until after the data has been read.
return errors.New("tar of sparse alternate data streams is unsupported")
}
case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
// ignore these streams
default:
return fmt.Errorf("%s: unknown stream ID %d after data", name, bhdr.Id)
}
}
return nil
}
// FileInfoFromHeader retrieves basic Win32 file information from a tar header, using the additional metadata written by
// WriteTarFileFromBackupStream.
func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) {
name = hdr.Name
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
size = hdr.Size
}
fileInfo = &winio.FileBasicInfo{
LastAccessTime: windows.NsecToFiletime(hdr.AccessTime.UnixNano()),
LastWriteTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()),
ChangeTime: windows.NsecToFiletime(hdr.ChangeTime.UnixNano()),
// Default to ModTime, we'll pull hdrCreationTime below if present
CreationTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()),
}
if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok {
attr, err := strconv.ParseUint(attrStr, 10, 32)
if err != nil {
return "", 0, nil, err
}
fileInfo.FileAttributes = uint32(attr)
} else {
if hdr.Typeflag == tar.TypeDir {
fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY
}
}
if creationTimeStr, ok := hdr.PAXRecords[hdrCreationTime]; ok {
creationTime, err := parsePAXTime(creationTimeStr)
if err != nil {
return "", 0, nil, err
}
fileInfo.CreationTime = windows.NsecToFiletime(creationTime.UnixNano())
}
return
}
// WriteBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple
// tar file entries in order to collect all the alternate data streams for the file, it returns the next
// tar file that was not processed, or io.EOF is there are no more.
func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) {
bw := winio.NewBackupStreamWriter(w)
var sd []byte
var err error
// Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written
// by this library will have raw binary for the security descriptor.
if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok {
sd, err = winio.SddlToSecurityDescriptor(sddl)
if err != nil {
return nil, err
}
}
if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok {
sd, err = base64.StdEncoding.DecodeString(sdraw)
if err != nil {
return nil, err
}
}
if len(sd) != 0 {
bhdr := winio.BackupHeader{
Id: winio.BackupSecurity,
Size: int64(len(sd)),
}
err := bw.WriteHeader(&bhdr)
if err != nil {
return nil, err
}
_, err = bw.Write(sd)
if err != nil {
return nil, err
}
}
var eas []winio.ExtendedAttribute
for k, v := range hdr.PAXRecords {
if !strings.HasPrefix(k, hdrEaPrefix) {
continue
}
data, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return nil, err
}
eas = append(eas, winio.ExtendedAttribute{
Name: k[len(hdrEaPrefix):],
Value: data,
})
}
if len(eas) != 0 {
eadata, err := winio.EncodeExtendedAttributes(eas)
if err != nil {
return nil, err
}
bhdr := winio.BackupHeader{
Id: winio.BackupEaData,
Size: int64(len(eadata)),
}
err = bw.WriteHeader(&bhdr)
if err != nil {
return nil, err
}
_, err = bw.Write(eadata)
if err != nil {
return nil, err
}
}
if hdr.Typeflag == tar.TypeSymlink {
_, isMountPoint := hdr.PAXRecords[hdrMountPoint]
rp := winio.ReparsePoint{
Target: filepath.FromSlash(hdr.Linkname),
IsMountPoint: isMountPoint,
}
reparse := winio.EncodeReparsePoint(&rp)
bhdr := winio.BackupHeader{
Id: winio.BackupReparseData,
Size: int64(len(reparse)),
}
err := bw.WriteHeader(&bhdr)
if err != nil {
return nil, err
}
_, err = bw.Write(reparse)
if err != nil {
return nil, err
}
}
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
bhdr := winio.BackupHeader{
Id: winio.BackupData,
Size: hdr.Size,
}
err := bw.WriteHeader(&bhdr)
if err != nil {
return nil, err
}
_, err = io.Copy(bw, t)
if err != nil {
return nil, err
}
}
// Copy all the alternate data streams and return the next non-ADS header.
for {
ahdr, err := t.Next()
if err != nil {
return nil, err
}
if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
return ahdr, nil
}
bhdr := winio.BackupHeader{
Id: winio.BackupAlternateData,
Size: ahdr.Size,
Name: ahdr.Name[len(hdr.Name):] + ":$DATA",
}
err = bw.WriteHeader(&bhdr)
if err != nil {
return nil, err
}
_, err = io.Copy(bw, t)
if err != nil {
return nil, err
}
}
}

View File

@ -1,137 +0,0 @@
package winio
import (
"bytes"
"encoding/binary"
"errors"
)
type fileFullEaInformation struct {
NextEntryOffset uint32
Flags uint8
NameLength uint8
ValueLength uint16
}
var (
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
errEaNameTooLarge = errors.New("extended attribute name too large")
errEaValueTooLarge = errors.New("extended attribute value too large")
)
// ExtendedAttribute represents a single Windows EA.
type ExtendedAttribute struct {
Name string
Value []byte
Flags uint8
}
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
var info fileFullEaInformation
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
if err != nil {
err = errInvalidEaBuffer
return
}
nameOffset := fileFullEaInformationSize
nameLen := int(info.NameLength)
valueOffset := nameOffset + int(info.NameLength) + 1
valueLen := int(info.ValueLength)
nextOffset := int(info.NextEntryOffset)
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
err = errInvalidEaBuffer
return
}
ea.Name = string(b[nameOffset : nameOffset+nameLen])
ea.Value = b[valueOffset : valueOffset+valueLen]
ea.Flags = info.Flags
if info.NextEntryOffset != 0 {
nb = b[info.NextEntryOffset:]
}
return
}
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
for len(b) != 0 {
ea, nb, err := parseEa(b)
if err != nil {
return nil, err
}
eas = append(eas, ea)
b = nb
}
return
}
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
if int(uint8(len(ea.Name))) != len(ea.Name) {
return errEaNameTooLarge
}
if int(uint16(len(ea.Value))) != len(ea.Value) {
return errEaValueTooLarge
}
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
withPadding := (entrySize + 3) &^ 3
nextOffset := uint32(0)
if !last {
nextOffset = withPadding
}
info := fileFullEaInformation{
NextEntryOffset: nextOffset,
Flags: ea.Flags,
NameLength: uint8(len(ea.Name)),
ValueLength: uint16(len(ea.Value)),
}
err := binary.Write(buf, binary.LittleEndian, &info)
if err != nil {
return err
}
_, err = buf.Write([]byte(ea.Name))
if err != nil {
return err
}
err = buf.WriteByte(0)
if err != nil {
return err
}
_, err = buf.Write(ea.Value)
if err != nil {
return err
}
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
if err != nil {
return err
}
return nil
}
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
// buffer for use with BackupWrite, ZwSetEaFile, etc.
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
var buf bytes.Buffer
for i := range eas {
last := false
if i == len(eas)-1 {
last = true
}
err := writeEa(&buf, &eas[i], last)
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}

View File

@ -1,323 +0,0 @@
// +build windows
package winio
import (
"errors"
"io"
"runtime"
"sync"
"sync/atomic"
"syscall"
"time"
)
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
type atomicBool int32
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
func (b *atomicBool) swap(new bool) bool {
var newInt int32
if new {
newInt = 1
}
return atomic.SwapInt32((*int32)(b), newInt) == 1
}
const (
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
)
var (
ErrFileClosed = errors.New("file has already been closed")
ErrTimeout = &timeoutError{}
)
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
type timeoutChan chan struct{}
var ioInitOnce sync.Once
var ioCompletionPort syscall.Handle
// ioResult contains the result of an asynchronous IO operation
type ioResult struct {
bytes uint32
err error
}
// ioOperation represents an outstanding asynchronous Win32 IO
type ioOperation struct {
o syscall.Overlapped
ch chan ioResult
}
func initIo() {
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
if err != nil {
panic(err)
}
ioCompletionPort = h
go ioCompletionProcessor(h)
}
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
// It takes ownership of this handle and will close it if it is garbage collected.
type win32File struct {
handle syscall.Handle
wg sync.WaitGroup
wgLock sync.RWMutex
closing atomicBool
socket bool
readDeadline deadlineHandler
writeDeadline deadlineHandler
}
type deadlineHandler struct {
setLock sync.Mutex
channel timeoutChan
channelLock sync.RWMutex
timer *time.Timer
timedout atomicBool
}
// makeWin32File makes a new win32File from an existing file handle
func makeWin32File(h syscall.Handle) (*win32File, error) {
f := &win32File{handle: h}
ioInitOnce.Do(initIo)
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
if err != nil {
return nil, err
}
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
if err != nil {
return nil, err
}
f.readDeadline.channel = make(timeoutChan)
f.writeDeadline.channel = make(timeoutChan)
return f, nil
}
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
// If we return the result of makeWin32File directly, it can result in an
// interface-wrapped nil, rather than a nil interface value.
f, err := makeWin32File(h)
if err != nil {
return nil, err
}
return f, nil
}
// closeHandle closes the resources associated with a Win32 handle
func (f *win32File) closeHandle() {
f.wgLock.Lock()
// Atomically set that we are closing, releasing the resources only once.
if !f.closing.swap(true) {
f.wgLock.Unlock()
// cancel all IO and wait for it to complete
cancelIoEx(f.handle, nil)
f.wg.Wait()
// at this point, no new IO can start
syscall.Close(f.handle)
f.handle = 0
} else {
f.wgLock.Unlock()
}
}
// Close closes a win32File.
func (f *win32File) Close() error {
f.closeHandle()
return nil
}
// prepareIo prepares for a new IO operation.
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
func (f *win32File) prepareIo() (*ioOperation, error) {
f.wgLock.RLock()
if f.closing.isSet() {
f.wgLock.RUnlock()
return nil, ErrFileClosed
}
f.wg.Add(1)
f.wgLock.RUnlock()
c := &ioOperation{}
c.ch = make(chan ioResult)
return c, nil
}
// ioCompletionProcessor processes completed async IOs forever
func ioCompletionProcessor(h syscall.Handle) {
for {
var bytes uint32
var key uintptr
var op *ioOperation
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
if op == nil {
panic(err)
}
op.ch <- ioResult{bytes, err}
}
}
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
// the operation has actually completed.
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
if err != syscall.ERROR_IO_PENDING {
return int(bytes), err
}
if f.closing.isSet() {
cancelIoEx(f.handle, &c.o)
}
var timeout timeoutChan
if d != nil {
d.channelLock.Lock()
timeout = d.channel
d.channelLock.Unlock()
}
var r ioResult
select {
case r = <-c.ch:
err = r.err
if err == syscall.ERROR_OPERATION_ABORTED {
if f.closing.isSet() {
err = ErrFileClosed
}
} else if err != nil && f.socket {
// err is from Win32. Query the overlapped structure to get the winsock error.
var bytes, flags uint32
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
}
case <-timeout:
cancelIoEx(f.handle, &c.o)
r = <-c.ch
err = r.err
if err == syscall.ERROR_OPERATION_ABORTED {
err = ErrTimeout
}
}
// runtime.KeepAlive is needed, as c is passed via native
// code to ioCompletionProcessor, c must remain alive
// until the channel read is complete.
runtime.KeepAlive(c)
return int(r.bytes), err
}
// Read reads from a file handle.
func (f *win32File) Read(b []byte) (int, error) {
c, err := f.prepareIo()
if err != nil {
return 0, err
}
defer f.wg.Done()
if f.readDeadline.timedout.isSet() {
return 0, ErrTimeout
}
var bytes uint32
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
runtime.KeepAlive(b)
// Handle EOF conditions.
if err == nil && n == 0 && len(b) != 0 {
return 0, io.EOF
} else if err == syscall.ERROR_BROKEN_PIPE {
return 0, io.EOF
} else {
return n, err
}
}
// Write writes to a file handle.
func (f *win32File) Write(b []byte) (int, error) {
c, err := f.prepareIo()
if err != nil {
return 0, err
}
defer f.wg.Done()
if f.writeDeadline.timedout.isSet() {
return 0, ErrTimeout
}
var bytes uint32
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
runtime.KeepAlive(b)
return n, err
}
func (f *win32File) SetReadDeadline(deadline time.Time) error {
return f.readDeadline.set(deadline)
}
func (f *win32File) SetWriteDeadline(deadline time.Time) error {
return f.writeDeadline.set(deadline)
}
func (f *win32File) Flush() error {
return syscall.FlushFileBuffers(f.handle)
}
func (f *win32File) Fd() uintptr {
return uintptr(f.handle)
}
func (d *deadlineHandler) set(deadline time.Time) error {
d.setLock.Lock()
defer d.setLock.Unlock()
if d.timer != nil {
if !d.timer.Stop() {
<-d.channel
}
d.timer = nil
}
d.timedout.setFalse()
select {
case <-d.channel:
d.channelLock.Lock()
d.channel = make(chan struct{})
d.channelLock.Unlock()
default:
}
if deadline.IsZero() {
return nil
}
timeoutIO := func() {
d.timedout.setTrue()
close(d.channel)
}
now := time.Now()
duration := deadline.Sub(now)
if deadline.After(now) {
// Deadline is in the future, set a timer to wait
d.timer = time.AfterFunc(duration, timeoutIO)
} else {
// Deadline is in the past. Cancel all pending IO now.
timeoutIO()
}
return nil
}

View File

@ -1,73 +0,0 @@
// +build windows
package winio
import (
"os"
"runtime"
"unsafe"
"golang.org/x/sys/windows"
)
// FileBasicInfo contains file access time and file attributes information.
type FileBasicInfo struct {
CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
FileAttributes uint32
pad uint32 // padding
}
// GetFileBasicInfo retrieves times and attributes for a file.
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
bi := &FileBasicInfo{}
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
}
runtime.KeepAlive(f)
return bi, nil
}
// SetFileBasicInfo sets times and attributes for a file.
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
}
runtime.KeepAlive(f)
return nil
}
// FileStandardInfo contains extended information for the file.
// FILE_STANDARD_INFO in WinBase.h
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
type FileStandardInfo struct {
AllocationSize, EndOfFile int64
NumberOfLinks uint32
DeletePending, Directory bool
}
// GetFileStandardInfo retrieves ended information for the file.
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
si := &FileStandardInfo{}
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil {
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
}
runtime.KeepAlive(f)
return si, nil
}
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
// unique on a system.
type FileIDInfo struct {
VolumeSerialNumber uint64
FileID [16]byte
}
// GetFileID retrieves the unique (volume, file ID) pair for a file.
func GetFileID(f *os.File) (*FileIDInfo, error) {
fileID := &FileIDInfo{}
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
}
runtime.KeepAlive(f)
return fileID, nil
}

View File

@ -1,9 +0,0 @@
module github.com/Microsoft/go-winio
go 1.12
require (
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.7.0
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
)

View File

@ -1,14 +0,0 @@
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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -1,307 +0,0 @@
// +build windows
package winio
import (
"fmt"
"io"
"net"
"os"
"syscall"
"time"
"unsafe"
"github.com/Microsoft/go-winio/pkg/guid"
)
//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
const (
afHvSock = 34 // AF_HYPERV
socketError = ^uintptr(0)
)
// An HvsockAddr is an address for a AF_HYPERV socket.
type HvsockAddr struct {
VMID guid.GUID
ServiceID guid.GUID
}
type rawHvsockAddr struct {
Family uint16
_ uint16
VMID guid.GUID
ServiceID guid.GUID
}
// Network returns the address's network name, "hvsock".
func (addr *HvsockAddr) Network() string {
return "hvsock"
}
func (addr *HvsockAddr) String() string {
return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
}
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
func VsockServiceID(port uint32) guid.GUID {
g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
g.Data1 = port
return g
}
func (addr *HvsockAddr) raw() rawHvsockAddr {
return rawHvsockAddr{
Family: afHvSock,
VMID: addr.VMID,
ServiceID: addr.ServiceID,
}
}
func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
addr.VMID = raw.VMID
addr.ServiceID = raw.ServiceID
}
// HvsockListener is a socket listener for the AF_HYPERV address family.
type HvsockListener struct {
sock *win32File
addr HvsockAddr
}
// HvsockConn is a connected socket of the AF_HYPERV address family.
type HvsockConn struct {
sock *win32File
local, remote HvsockAddr
}
func newHvSocket() (*win32File, error) {
fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1)
if err != nil {
return nil, os.NewSyscallError("socket", err)
}
f, err := makeWin32File(fd)
if err != nil {
syscall.Close(fd)
return nil, err
}
f.socket = true
return f, nil
}
// ListenHvsock listens for connections on the specified hvsock address.
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
l := &HvsockListener{addr: *addr}
sock, err := newHvSocket()
if err != nil {
return nil, l.opErr("listen", err)
}
sa := addr.raw()
err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa)))
if err != nil {
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
}
err = syscall.Listen(sock.handle, 16)
if err != nil {
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
}
return &HvsockListener{sock: sock, addr: *addr}, nil
}
func (l *HvsockListener) opErr(op string, err error) error {
return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
}
// Addr returns the listener's network address.
func (l *HvsockListener) Addr() net.Addr {
return &l.addr
}
// Accept waits for the next connection and returns it.
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
sock, err := newHvSocket()
if err != nil {
return nil, l.opErr("accept", err)
}
defer func() {
if sock != nil {
sock.Close()
}
}()
c, err := l.sock.prepareIo()
if err != nil {
return nil, l.opErr("accept", err)
}
defer l.sock.wg.Done()
// AcceptEx, per documentation, requires an extra 16 bytes per address.
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
var addrbuf [addrlen * 2]byte
var bytes uint32
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o)
_, err = l.sock.asyncIo(c, nil, bytes, err)
if err != nil {
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
}
conn := &HvsockConn{
sock: sock,
}
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
sock = nil
return conn, nil
}
// Close closes the listener, causing any pending Accept calls to fail.
func (l *HvsockListener) Close() error {
return l.sock.Close()
}
/* Need to finish ConnectEx handling
func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) {
sock, err := newHvSocket()
if err != nil {
return nil, err
}
defer func() {
if sock != nil {
sock.Close()
}
}()
c, err := sock.prepareIo()
if err != nil {
return nil, err
}
defer sock.wg.Done()
var bytes uint32
err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o)
_, err = sock.asyncIo(ctx, c, nil, bytes, err)
if err != nil {
return nil, err
}
conn := &HvsockConn{
sock: sock,
remote: *addr,
}
sock = nil
return conn, nil
}
*/
func (conn *HvsockConn) opErr(op string, err error) error {
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
}
func (conn *HvsockConn) Read(b []byte) (int, error) {
c, err := conn.sock.prepareIo()
if err != nil {
return 0, conn.opErr("read", err)
}
defer conn.sock.wg.Done()
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
var flags, bytes uint32
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err)
if err != nil {
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("wsarecv", err)
}
return 0, conn.opErr("read", err)
} else if n == 0 {
err = io.EOF
}
return n, err
}
func (conn *HvsockConn) Write(b []byte) (int, error) {
t := 0
for len(b) != 0 {
n, err := conn.write(b)
if err != nil {
return t + n, err
}
t += n
b = b[n:]
}
return t, nil
}
func (conn *HvsockConn) write(b []byte) (int, error) {
c, err := conn.sock.prepareIo()
if err != nil {
return 0, conn.opErr("write", err)
}
defer conn.sock.wg.Done()
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
var bytes uint32
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err)
if err != nil {
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("wsasend", err)
}
return 0, conn.opErr("write", err)
}
return n, err
}
// Close closes the socket connection, failing any pending read or write calls.
func (conn *HvsockConn) Close() error {
return conn.sock.Close()
}
func (conn *HvsockConn) shutdown(how int) error {
err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD)
if err != nil {
return os.NewSyscallError("shutdown", err)
}
return nil
}
// CloseRead shuts down the read end of the socket.
func (conn *HvsockConn) CloseRead() error {
err := conn.shutdown(syscall.SHUT_RD)
if err != nil {
return conn.opErr("close", err)
}
return nil
}
// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
// no more data will be written.
func (conn *HvsockConn) CloseWrite() error {
err := conn.shutdown(syscall.SHUT_WR)
if err != nil {
return conn.opErr("close", err)
}
return nil
}
// LocalAddr returns the local address of the connection.
func (conn *HvsockConn) LocalAddr() net.Addr {
return &conn.local
}
// RemoteAddr returns the remote address of the connection.
func (conn *HvsockConn) RemoteAddr() net.Addr {
return &conn.remote
}
// SetDeadline implements the net.Conn SetDeadline method.
func (conn *HvsockConn) SetDeadline(t time.Time) error {
conn.SetReadDeadline(t)
conn.SetWriteDeadline(t)
return nil
}
// SetReadDeadline implements the net.Conn SetReadDeadline method.
func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
return conn.sock.SetReadDeadline(t)
}
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
return conn.sock.SetWriteDeadline(t)
}

View File

@ -1,517 +0,0 @@
// +build windows
package winio
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"runtime"
"syscall"
"time"
"unsafe"
)
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
type ioStatusBlock struct {
Status, Information uintptr
}
type objectAttributes struct {
Length uintptr
RootDirectory uintptr
ObjectName *unicodeString
Attributes uintptr
SecurityDescriptor *securityDescriptor
SecurityQoS uintptr
}
type unicodeString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
type securityDescriptor struct {
Revision byte
Sbz1 byte
Control uint16
Owner uintptr
Group uintptr
Sacl uintptr
Dacl uintptr
}
type ntstatus int32
func (status ntstatus) Err() error {
if status >= 0 {
return nil
}
return rtlNtStatusToDosError(status)
}
const (
cERROR_PIPE_BUSY = syscall.Errno(231)
cERROR_NO_DATA = syscall.Errno(232)
cERROR_PIPE_CONNECTED = syscall.Errno(535)
cERROR_SEM_TIMEOUT = syscall.Errno(121)
cSECURITY_SQOS_PRESENT = 0x100000
cSECURITY_ANONYMOUS = 0
cPIPE_TYPE_MESSAGE = 4
cPIPE_READMODE_MESSAGE = 2
cFILE_OPEN = 1
cFILE_CREATE = 2
cFILE_PIPE_MESSAGE_TYPE = 1
cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2
cSE_DACL_PRESENT = 4
)
var (
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
// This error should match net.errClosing since docker takes a dependency on its text.
ErrPipeListenerClosed = errors.New("use of closed network connection")
errPipeWriteClosed = errors.New("pipe has been closed for write")
)
type win32Pipe struct {
*win32File
path string
}
type win32MessageBytePipe struct {
win32Pipe
writeClosed bool
readEOF bool
}
type pipeAddress string
func (f *win32Pipe) LocalAddr() net.Addr {
return pipeAddress(f.path)
}
func (f *win32Pipe) RemoteAddr() net.Addr {
return pipeAddress(f.path)
}
func (f *win32Pipe) SetDeadline(t time.Time) error {
f.SetReadDeadline(t)
f.SetWriteDeadline(t)
return nil
}
// CloseWrite closes the write side of a message pipe in byte mode.
func (f *win32MessageBytePipe) CloseWrite() error {
if f.writeClosed {
return errPipeWriteClosed
}
err := f.win32File.Flush()
if err != nil {
return err
}
_, err = f.win32File.Write(nil)
if err != nil {
return err
}
f.writeClosed = true
return nil
}
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
// they are used to implement CloseWrite().
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
if f.writeClosed {
return 0, errPipeWriteClosed
}
if len(b) == 0 {
return 0, nil
}
return f.win32File.Write(b)
}
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
// mode pipe will return io.EOF, as will all subsequent reads.
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
if f.readEOF {
return 0, io.EOF
}
n, err := f.win32File.Read(b)
if err == io.EOF {
// If this was the result of a zero-byte read, then
// it is possible that the read was due to a zero-size
// message. Since we are simulating CloseWrite with a
// zero-byte message, ensure that all future Read() calls
// also return EOF.
f.readEOF = true
} else if err == syscall.ERROR_MORE_DATA {
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
// and the message still has more bytes. Treat this as a success, since
// this package presents all named pipes as byte streams.
err = nil
}
return n, err
}
func (s pipeAddress) Network() string {
return "pipe"
}
func (s pipeAddress) String() string {
return string(s)
}
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) {
for {
select {
case <-ctx.Done():
return syscall.Handle(0), ctx.Err()
default:
h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err == nil {
return h, nil
}
if err != cERROR_PIPE_BUSY {
return h, &os.PathError{Err: err, Op: "open", Path: *path}
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
time.Sleep(10 * time.Millisecond)
}
}
}
// DialPipe connects to a named pipe by path, timing out if the connection
// takes longer than the specified duration. If timeout is nil, then we use
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
var absTimeout time.Time
if timeout != nil {
absTimeout = time.Now().Add(*timeout)
} else {
absTimeout = time.Now().Add(2 * time.Second)
}
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
conn, err := DialPipeContext(ctx, path)
if err == context.DeadlineExceeded {
return nil, ErrTimeout
}
return conn, err
}
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
// cancellation or timeout.
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
}
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
// cancellation or timeout.
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
var err error
var h syscall.Handle
h, err = tryDialPipe(ctx, &path, access)
if err != nil {
return nil, err
}
var flags uint32
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
if err != nil {
return nil, err
}
f, err := makeWin32File(h)
if err != nil {
syscall.Close(h)
return nil, err
}
// If the pipe is in message mode, return a message byte pipe, which
// supports CloseWrite().
if flags&cPIPE_TYPE_MESSAGE != 0 {
return &win32MessageBytePipe{
win32Pipe: win32Pipe{win32File: f, path: path},
}, nil
}
return &win32Pipe{win32File: f, path: path}, nil
}
type acceptResponse struct {
f *win32File
err error
}
type win32PipeListener struct {
firstHandle syscall.Handle
path string
config PipeConfig
acceptCh chan (chan acceptResponse)
closeCh chan int
doneCh chan int
}
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
path16, err := syscall.UTF16FromString(path)
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
var oa objectAttributes
oa.Length = unsafe.Sizeof(oa)
var ntPath unicodeString
if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
defer localFree(ntPath.Buffer)
oa.ObjectName = &ntPath
// The security descriptor is only needed for the first pipe.
if first {
if sd != nil {
len := uint32(len(sd))
sdb := localAlloc(0, len)
defer localFree(sdb)
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
} else {
// Construct the default named pipe security descriptor.
var dacl uintptr
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
return 0, fmt.Errorf("getting default named pipe ACL: %s", err)
}
defer localFree(dacl)
sdb := &securityDescriptor{
Revision: 1,
Control: cSE_DACL_PRESENT,
Dacl: dacl,
}
oa.SecurityDescriptor = sdb
}
}
typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS)
if c.MessageMode {
typ |= cFILE_PIPE_MESSAGE_TYPE
}
disposition := uint32(cFILE_OPEN)
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
if first {
disposition = cFILE_CREATE
// By not asking for read or write access, the named pipe file system
// will put this pipe into an initially disconnected state, blocking
// client connections until the next call with first == false.
access = syscall.SYNCHRONIZE
}
timeout := int64(-50 * 10000) // 50ms
var (
h syscall.Handle
iosb ioStatusBlock
)
err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err()
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
runtime.KeepAlive(ntPath)
return h, nil
}
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
if err != nil {
return nil, err
}
f, err := makeWin32File(h)
if err != nil {
syscall.Close(h)
return nil, err
}
return f, nil
}
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
p, err := l.makeServerPipe()
if err != nil {
return nil, err
}
// Wait for the client to connect.
ch := make(chan error)
go func(p *win32File) {
ch <- connectPipe(p)
}(p)
select {
case err = <-ch:
if err != nil {
p.Close()
p = nil
}
case <-l.closeCh:
// Abort the connect request by closing the handle.
p.Close()
p = nil
err = <-ch
if err == nil || err == ErrFileClosed {
err = ErrPipeListenerClosed
}
}
return p, err
}
func (l *win32PipeListener) listenerRoutine() {
closed := false
for !closed {
select {
case <-l.closeCh:
closed = true
case responseCh := <-l.acceptCh:
var (
p *win32File
err error
)
for {
p, err = l.makeConnectedServerPipe()
// If the connection was immediately closed by the client, try
// again.
if err != cERROR_NO_DATA {
break
}
}
responseCh <- acceptResponse{p, err}
closed = err == ErrPipeListenerClosed
}
}
syscall.Close(l.firstHandle)
l.firstHandle = 0
// Notify Close() and Accept() callers that the handle has been closed.
close(l.doneCh)
}
// PipeConfig contain configuration for the pipe listener.
type PipeConfig struct {
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
SecurityDescriptor string
// MessageMode determines whether the pipe is in byte or message mode. In either
// case the pipe is read in byte mode by default. The only practical difference in
// this implementation is that CloseWrite() is only supported for message mode pipes;
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
// transferred to the reader (and returned as io.EOF in this implementation)
// when the pipe is in message mode.
MessageMode bool
// InputBufferSize specifies the size of the input buffer, in bytes.
InputBufferSize int32
// OutputBufferSize specifies the size of the output buffer, in bytes.
OutputBufferSize int32
}
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
// The pipe must not already exist.
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
var (
sd []byte
err error
)
if c == nil {
c = &PipeConfig{}
}
if c.SecurityDescriptor != "" {
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
if err != nil {
return nil, err
}
}
h, err := makeServerPipeHandle(path, sd, c, true)
if err != nil {
return nil, err
}
l := &win32PipeListener{
firstHandle: h,
path: path,
config: *c,
acceptCh: make(chan (chan acceptResponse)),
closeCh: make(chan int),
doneCh: make(chan int),
}
go l.listenerRoutine()
return l, nil
}
func connectPipe(p *win32File) error {
c, err := p.prepareIo()
if err != nil {
return err
}
defer p.wg.Done()
err = connectNamedPipe(p.handle, &c.o)
_, err = p.asyncIo(c, nil, 0, err)
if err != nil && err != cERROR_PIPE_CONNECTED {
return err
}
return nil
}
func (l *win32PipeListener) Accept() (net.Conn, error) {
ch := make(chan acceptResponse)
select {
case l.acceptCh <- ch:
response := <-ch
err := response.err
if err != nil {
return nil, err
}
if l.config.MessageMode {
return &win32MessageBytePipe{
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
}, nil
}
return &win32Pipe{win32File: response.f, path: l.path}, nil
case <-l.doneCh:
return nil, ErrPipeListenerClosed
}
}
func (l *win32PipeListener) Close() error {
select {
case l.closeCh <- 1:
<-l.doneCh
case <-l.doneCh:
}
return nil
}
func (l *win32PipeListener) Addr() net.Addr {
return pipeAddress(l.path)
}

View File

@ -1,237 +0,0 @@
// +build windows
// Package guid provides a GUID type. The backing structure for a GUID is
// identical to that used by the golang.org/x/sys/windows GUID type.
// There are two main binary encodings used for a GUID, the big-endian encoding,
// and the Windows (mixed-endian) encoding. See here for details:
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
package guid
import (
"crypto/rand"
"crypto/sha1"
"encoding"
"encoding/binary"
"fmt"
"strconv"
"golang.org/x/sys/windows"
)
// Variant specifies which GUID variant (or "type") of the GUID. It determines
// how the entirety of the rest of the GUID is interpreted.
type Variant uint8
// The variants specified by RFC 4122.
const (
// VariantUnknown specifies a GUID variant which does not conform to one of
// the variant encodings specified in RFC 4122.
VariantUnknown Variant = iota
VariantNCS
VariantRFC4122
VariantMicrosoft
VariantFuture
)
// Version specifies how the bits in the GUID were generated. For instance, a
// version 4 GUID is randomly generated, and a version 5 is generated from the
// hash of an input string.
type Version uint8
var _ = (encoding.TextMarshaler)(GUID{})
var _ = (encoding.TextUnmarshaler)(&GUID{})
// GUID represents a GUID/UUID. It has the same structure as
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
// that type. It is defined as its own type so that stringification and
// marshaling can be supported. The representation matches that used by native
// Windows code.
type GUID windows.GUID
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
func NewV4() (GUID, error) {
var b [16]byte
if _, err := rand.Read(b[:]); err != nil {
return GUID{}, err
}
g := FromArray(b)
g.setVersion(4) // Version 4 means randomly generated.
g.setVariant(VariantRFC4122)
return g, nil
}
// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
// and the sample code treats it as a series of bytes, so we do the same here.
//
// Some implementations, such as those found on Windows, treat the name as a
// big-endian UTF16 stream of bytes. If that is desired, the string can be
// encoded as such before being passed to this function.
func NewV5(namespace GUID, name []byte) (GUID, error) {
b := sha1.New()
namespaceBytes := namespace.ToArray()
b.Write(namespaceBytes[:])
b.Write(name)
a := [16]byte{}
copy(a[:], b.Sum(nil))
g := FromArray(a)
g.setVersion(5) // Version 5 means generated from a string.
g.setVariant(VariantRFC4122)
return g, nil
}
func fromArray(b [16]byte, order binary.ByteOrder) GUID {
var g GUID
g.Data1 = order.Uint32(b[0:4])
g.Data2 = order.Uint16(b[4:6])
g.Data3 = order.Uint16(b[6:8])
copy(g.Data4[:], b[8:16])
return g
}
func (g GUID) toArray(order binary.ByteOrder) [16]byte {
b := [16]byte{}
order.PutUint32(b[0:4], g.Data1)
order.PutUint16(b[4:6], g.Data2)
order.PutUint16(b[6:8], g.Data3)
copy(b[8:16], g.Data4[:])
return b
}
// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
func FromArray(b [16]byte) GUID {
return fromArray(b, binary.BigEndian)
}
// ToArray returns an array of 16 bytes representing the GUID in big-endian
// encoding.
func (g GUID) ToArray() [16]byte {
return g.toArray(binary.BigEndian)
}
// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
func FromWindowsArray(b [16]byte) GUID {
return fromArray(b, binary.LittleEndian)
}
// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
// encoding.
func (g GUID) ToWindowsArray() [16]byte {
return g.toArray(binary.LittleEndian)
}
func (g GUID) String() string {
return fmt.Sprintf(
"%08x-%04x-%04x-%04x-%012x",
g.Data1,
g.Data2,
g.Data3,
g.Data4[:2],
g.Data4[2:])
}
// FromString parses a string containing a GUID and returns the GUID. The only
// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
// format.
func FromString(s string) (GUID, error) {
if len(s) != 36 {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
var g GUID
data1, err := strconv.ParseUint(s[0:8], 16, 32)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data1 = uint32(data1)
data2, err := strconv.ParseUint(s[9:13], 16, 16)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data2 = uint16(data2)
data3, err := strconv.ParseUint(s[14:18], 16, 16)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data3 = uint16(data3)
for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
v, err := strconv.ParseUint(s[x:x+2], 16, 8)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data4[i] = uint8(v)
}
return g, nil
}
func (g *GUID) setVariant(v Variant) {
d := g.Data4[0]
switch v {
case VariantNCS:
d = (d & 0x7f)
case VariantRFC4122:
d = (d & 0x3f) | 0x80
case VariantMicrosoft:
d = (d & 0x1f) | 0xc0
case VariantFuture:
d = (d & 0x0f) | 0xe0
case VariantUnknown:
fallthrough
default:
panic(fmt.Sprintf("invalid variant: %d", v))
}
g.Data4[0] = d
}
// Variant returns the GUID variant, as defined in RFC 4122.
func (g GUID) Variant() Variant {
b := g.Data4[0]
if b&0x80 == 0 {
return VariantNCS
} else if b&0xc0 == 0x80 {
return VariantRFC4122
} else if b&0xe0 == 0xc0 {
return VariantMicrosoft
} else if b&0xe0 == 0xe0 {
return VariantFuture
}
return VariantUnknown
}
func (g *GUID) setVersion(v Version) {
g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
}
// Version returns the GUID version, as defined in RFC 4122.
func (g GUID) Version() Version {
return Version((g.Data3 & 0xF000) >> 12)
}
// MarshalText returns the textual representation of the GUID.
func (g GUID) MarshalText() ([]byte, error) {
return []byte(g.String()), nil
}
// UnmarshalText takes the textual representation of a GUID, and unmarhals it
// into this GUID.
func (g *GUID) UnmarshalText(text []byte) error {
g2, err := FromString(string(text))
if err != nil {
return err
}
*g = g2
return nil
}

View File

@ -1,161 +0,0 @@
// +build windows
package security
import (
"os"
"syscall"
"unsafe"
"github.com/pkg/errors"
)
type (
accessMask uint32
accessMode uint32
desiredAccess uint32
inheritMode uint32
objectType uint32
shareMode uint32
securityInformation uint32
trusteeForm uint32
trusteeType uint32
explicitAccess struct {
accessPermissions accessMask
accessMode accessMode
inheritance inheritMode
trustee trustee
}
trustee struct {
multipleTrustee *trustee
multipleTrusteeOperation int32
trusteeForm trusteeForm
trusteeType trusteeType
name uintptr
}
)
const (
accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ
accessModeGrant accessMode = 1
desiredAccessReadControl desiredAccess = 0x20000
desiredAccessWriteDac desiredAccess = 0x40000
gvmga = "GrantVmGroupAccess:"
inheritModeNoInheritance inheritMode = 0x0
inheritModeSubContainersAndObjectsInherit inheritMode = 0x3
objectTypeFileObject objectType = 0x1
securityInformationDACL securityInformation = 0x4
shareModeRead shareMode = 0x1
shareModeWrite shareMode = 0x2
sidVmGroup = "S-1-5-83-0"
trusteeFormIsSid trusteeForm = 0
trusteeTypeWellKnownGroup trusteeType = 5
)
// GrantVMGroupAccess sets the DACL for a specified file or directory to
// include Grant ACE entries for the VM Group SID. This is a golang re-
// implementation of the same function in vmcompute, just not exported in
// RS5. Which kind of sucks. Sucks a lot :/
func GrantVmGroupAccess(name string) error {
// Stat (to determine if `name` is a directory).
s, err := os.Stat(name)
if err != nil {
return errors.Wrapf(err, "%s os.Stat %s", gvmga, name)
}
// Get a handle to the file/directory. Must defer Close on success.
fd, err := createFile(name, s.IsDir())
if err != nil {
return err // Already wrapped
}
defer syscall.CloseHandle(fd)
// Get the current DACL and Security Descriptor. Must defer LocalFree on success.
ot := objectTypeFileObject
si := securityInformationDACL
sd := uintptr(0)
origDACL := uintptr(0)
if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil {
return errors.Wrapf(err, "%s GetSecurityInfo %s", gvmga, name)
}
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd)))
// Generate a new DACL which is the current DACL with the required ACEs added.
// Must defer LocalFree on success.
newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL)
if err != nil {
return err // Already wrapped
}
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL)))
// And finally use SetSecurityInfo to apply the updated DACL.
if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil {
return errors.Wrapf(err, "%s SetSecurityInfo %s", gvmga, name)
}
return nil
}
// createFile is a helper function to call [Nt]CreateFile to get a handle to
// the file or directory.
func createFile(name string, isDir bool) (syscall.Handle, error) {
namep := syscall.StringToUTF16(name)
da := uint32(desiredAccessReadControl | desiredAccessWriteDac)
sm := uint32(shareModeRead | shareModeWrite)
fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL)
if isDir {
fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS)
}
fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0)
if err != nil {
return 0, errors.Wrapf(err, "%s syscall.CreateFile %s", gvmga, name)
}
return fd, nil
}
// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added.
// The caller is responsible for LocalFree of the returned DACL on success.
func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) {
// Generate pointers to the SIDs based on the string SIDs
sid, err := syscall.StringToSid(sidVmGroup)
if err != nil {
return 0, errors.Wrapf(err, "%s syscall.StringToSid %s %s", gvmga, name, sidVmGroup)
}
inheritance := inheritModeNoInheritance
if isDir {
inheritance = inheritModeSubContainersAndObjectsInherit
}
eaArray := []explicitAccess{
explicitAccess{
accessPermissions: accessMaskDesiredPermission,
accessMode: accessModeGrant,
inheritance: inheritance,
trustee: trustee{
trusteeForm: trusteeFormIsSid,
trusteeType: trusteeTypeWellKnownGroup,
name: uintptr(unsafe.Pointer(sid)),
},
},
}
modifiedDACL := uintptr(0)
if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil {
return 0, errors.Wrapf(err, "%s SetEntriesInAcl %s", gvmga, name)
}
return modifiedDACL, nil
}

View File

@ -1,7 +0,0 @@
package security
//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
//sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo
//sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) [failretval!=0] = advapi32.SetSecurityInfo
//sys setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) [failretval!=0] = advapi32.SetEntriesInAclW

View File

@ -1,70 +0,0 @@
// Code generated by 'go generate'; DO NOT EDIT.
package security
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo")
procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW")
procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo")
)
func getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) {
r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(ppsidOwner)), uintptr(unsafe.Pointer(ppsidGroup)), uintptr(unsafe.Pointer(ppDacl)), uintptr(unsafe.Pointer(ppSacl)), uintptr(unsafe.Pointer(ppSecurityDescriptor)), 0)
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) {
r1, _, e1 := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(count), uintptr(pListOfEEs), uintptr(oldAcl), uintptr(unsafe.Pointer(newAcl)), 0, 0)
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) {
r1, _, e1 := syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(psidOwner), uintptr(psidGroup), uintptr(pDacl), uintptr(pSacl), 0, 0)
if r1 != 0 {
err = errnoErr(e1)
}
return
}

View File

@ -1,202 +0,0 @@
// +build windows
package winio
import (
"bytes"
"encoding/binary"
"fmt"
"runtime"
"sync"
"syscall"
"unicode/utf16"
"golang.org/x/sys/windows"
)
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
//sys revertToSelf() (err error) = advapi32.RevertToSelf
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
const (
SE_PRIVILEGE_ENABLED = 2
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
SeBackupPrivilege = "SeBackupPrivilege"
SeRestorePrivilege = "SeRestorePrivilege"
)
const (
securityAnonymous = iota
securityIdentification
securityImpersonation
securityDelegation
)
var (
privNames = make(map[string]uint64)
privNameMutex sync.Mutex
)
// PrivilegeError represents an error enabling privileges.
type PrivilegeError struct {
privileges []uint64
}
func (e *PrivilegeError) Error() string {
s := ""
if len(e.privileges) > 1 {
s = "Could not enable privileges "
} else {
s = "Could not enable privilege "
}
for i, p := range e.privileges {
if i != 0 {
s += ", "
}
s += `"`
s += getPrivilegeName(p)
s += `"`
}
return s
}
// RunWithPrivilege enables a single privilege for a function call.
func RunWithPrivilege(name string, fn func() error) error {
return RunWithPrivileges([]string{name}, fn)
}
// RunWithPrivileges enables privileges for a function call.
func RunWithPrivileges(names []string, fn func() error) error {
privileges, err := mapPrivileges(names)
if err != nil {
return err
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
token, err := newThreadToken()
if err != nil {
return err
}
defer releaseThreadToken(token)
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
if err != nil {
return err
}
return fn()
}
func mapPrivileges(names []string) ([]uint64, error) {
var privileges []uint64
privNameMutex.Lock()
defer privNameMutex.Unlock()
for _, name := range names {
p, ok := privNames[name]
if !ok {
err := lookupPrivilegeValue("", name, &p)
if err != nil {
return nil, err
}
privNames[name] = p
}
privileges = append(privileges, p)
}
return privileges, nil
}
// EnableProcessPrivileges enables privileges globally for the process.
func EnableProcessPrivileges(names []string) error {
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
}
// DisableProcessPrivileges disables privileges globally for the process.
func DisableProcessPrivileges(names []string) error {
return enableDisableProcessPrivilege(names, 0)
}
func enableDisableProcessPrivilege(names []string, action uint32) error {
privileges, err := mapPrivileges(names)
if err != nil {
return err
}
p, _ := windows.GetCurrentProcess()
var token windows.Token
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
if err != nil {
return err
}
defer token.Close()
return adjustPrivileges(token, privileges, action)
}
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
var b bytes.Buffer
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
for _, p := range privileges {
binary.Write(&b, binary.LittleEndian, p)
binary.Write(&b, binary.LittleEndian, action)
}
prevState := make([]byte, b.Len())
reqSize := uint32(0)
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
if !success {
return err
}
if err == ERROR_NOT_ALL_ASSIGNED {
return &PrivilegeError{privileges}
}
return nil
}
func getPrivilegeName(luid uint64) string {
var nameBuffer [256]uint16
bufSize := uint32(len(nameBuffer))
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
if err != nil {
return fmt.Sprintf("<unknown privilege %d>", luid)
}
var displayNameBuffer [256]uint16
displayBufSize := uint32(len(displayNameBuffer))
var langID uint32
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
if err != nil {
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
}
return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
}
func newThreadToken() (windows.Token, error) {
err := impersonateSelf(securityImpersonation)
if err != nil {
return 0, err
}
var token windows.Token
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
if err != nil {
rerr := revertToSelf()
if rerr != nil {
panic(rerr)
}
return 0, err
}
return token, nil
}
func releaseThreadToken(h windows.Token) {
err := revertToSelf()
if err != nil {
panic(err)
}
h.Close()
}

View File

@ -1,128 +0,0 @@
package winio
import (
"bytes"
"encoding/binary"
"fmt"
"strings"
"unicode/utf16"
"unsafe"
)
const (
reparseTagMountPoint = 0xA0000003
reparseTagSymlink = 0xA000000C
)
type reparseDataBuffer struct {
ReparseTag uint32
ReparseDataLength uint16
Reserved uint16
SubstituteNameOffset uint16
SubstituteNameLength uint16
PrintNameOffset uint16
PrintNameLength uint16
}
// ReparsePoint describes a Win32 symlink or mount point.
type ReparsePoint struct {
Target string
IsMountPoint bool
}
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
// mount point reparse point.
type UnsupportedReparsePointError struct {
Tag uint32
}
func (e *UnsupportedReparsePointError) Error() string {
return fmt.Sprintf("unsupported reparse point %x", e.Tag)
}
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
// or a mount point.
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
tag := binary.LittleEndian.Uint32(b[0:4])
return DecodeReparsePointData(tag, b[8:])
}
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
isMountPoint := false
switch tag {
case reparseTagMountPoint:
isMountPoint = true
case reparseTagSymlink:
default:
return nil, &UnsupportedReparsePointError{tag}
}
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
if !isMountPoint {
nameOffset += 4
}
nameLength := binary.LittleEndian.Uint16(b[6:8])
name := make([]uint16, nameLength/2)
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
if err != nil {
return nil, err
}
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
}
func isDriveLetter(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
// mount point.
func EncodeReparsePoint(rp *ReparsePoint) []byte {
// Generate an NT path and determine if this is a relative path.
var ntTarget string
relative := false
if strings.HasPrefix(rp.Target, `\\?\`) {
ntTarget = `\??\` + rp.Target[4:]
} else if strings.HasPrefix(rp.Target, `\\`) {
ntTarget = `\??\UNC\` + rp.Target[2:]
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
ntTarget = `\??\` + rp.Target
} else {
ntTarget = rp.Target
relative = true
}
// The paths must be NUL-terminated even though they are counted strings.
target16 := utf16.Encode([]rune(rp.Target + "\x00"))
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
size += len(ntTarget16)*2 + len(target16)*2
tag := uint32(reparseTagMountPoint)
if !rp.IsMountPoint {
tag = reparseTagSymlink
size += 4 // Add room for symlink flags
}
data := reparseDataBuffer{
ReparseTag: tag,
ReparseDataLength: uint16(size),
SubstituteNameOffset: 0,
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
PrintNameOffset: uint16(len(ntTarget16) * 2),
PrintNameLength: uint16((len(target16) - 1) * 2),
}
var b bytes.Buffer
binary.Write(&b, binary.LittleEndian, &data)
if !rp.IsMountPoint {
flags := uint32(0)
if relative {
flags |= 1
}
binary.Write(&b, binary.LittleEndian, flags)
}
binary.Write(&b, binary.LittleEndian, ntTarget16)
binary.Write(&b, binary.LittleEndian, target16)
return b.Bytes()
}

View File

@ -1,98 +0,0 @@
// +build windows
package winio
import (
"syscall"
"unsafe"
)
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
//sys localFree(mem uintptr) = LocalFree
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
const (
cERROR_NONE_MAPPED = syscall.Errno(1332)
)
type AccountLookupError struct {
Name string
Err error
}
func (e *AccountLookupError) Error() string {
if e.Name == "" {
return "lookup account: empty account name specified"
}
var s string
switch e.Err {
case cERROR_NONE_MAPPED:
s = "not found"
default:
s = e.Err.Error()
}
return "lookup account " + e.Name + ": " + s
}
type SddlConversionError struct {
Sddl string
Err error
}
func (e *SddlConversionError) Error() string {
return "convert " + e.Sddl + ": " + e.Err.Error()
}
// LookupSidByName looks up the SID of an account by name
func LookupSidByName(name string) (sid string, err error) {
if name == "" {
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
}
var sidSize, sidNameUse, refDomainSize uint32
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
return "", &AccountLookupError{name, err}
}
sidBuffer := make([]byte, sidSize)
refDomainBuffer := make([]uint16, refDomainSize)
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
if err != nil {
return "", &AccountLookupError{name, err}
}
var strBuffer *uint16
err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
if err != nil {
return "", &AccountLookupError{name, err}
}
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
localFree(uintptr(unsafe.Pointer(strBuffer)))
return sid, nil
}
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
var sdBuffer uintptr
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
if err != nil {
return nil, &SddlConversionError{sddl, err}
}
defer localFree(sdBuffer)
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
return sd, nil
}
func SecurityDescriptorToSddl(sd []byte) (string, error) {
var sddl *uint16
// The returned string length seems to including an aribtrary number of terminating NULs.
// Don't use it.
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
if err != nil {
return "", err
}
defer localFree(uintptr(unsafe.Pointer(sddl)))
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
}

View File

@ -1,3 +0,0 @@
package winio
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go

View File

@ -1,323 +0,0 @@
// +build windows
package vhd
import (
"fmt"
"syscall"
"github.com/Microsoft/go-winio/pkg/guid"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
)
//go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go
//sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.CreateVirtualDisk
//sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.OpenVirtualDisk
//sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (err error) [failretval != 0] = virtdisk.AttachVirtualDisk
//sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (err error) [failretval != 0] = virtdisk.DetachVirtualDisk
//sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (err error) [failretval != 0] = virtdisk.GetVirtualDiskPhysicalPath
type (
CreateVirtualDiskFlag uint32
VirtualDiskFlag uint32
AttachVirtualDiskFlag uint32
DetachVirtualDiskFlag uint32
VirtualDiskAccessMask uint32
)
type VirtualStorageType struct {
DeviceID uint32
VendorID guid.GUID
}
type CreateVersion2 struct {
UniqueID guid.GUID
MaximumSize uint64
BlockSizeInBytes uint32
SectorSizeInBytes uint32
PhysicalSectorSizeInByte uint32
ParentPath *uint16 // string
SourcePath *uint16 // string
OpenFlags uint32
ParentVirtualStorageType VirtualStorageType
SourceVirtualStorageType VirtualStorageType
ResiliencyGUID guid.GUID
}
type CreateVirtualDiskParameters struct {
Version uint32 // Must always be set to 2
Version2 CreateVersion2
}
type OpenVersion2 struct {
GetInfoOnly bool
ReadOnly bool
ResiliencyGUID guid.GUID
}
type OpenVirtualDiskParameters struct {
Version uint32 // Must always be set to 2
Version2 OpenVersion2
}
type AttachVersion2 struct {
RestrictedOffset uint64
RestrictedLength uint64
}
type AttachVirtualDiskParameters struct {
Version uint32 // Must always be set to 2
Version2 AttachVersion2
}
const (
VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3
// Access Mask for opening a VHD
VirtualDiskAccessNone VirtualDiskAccessMask = 0x00000000
VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000
VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000
VirtualDiskAccessDetach VirtualDiskAccessMask = 0x00040000
VirtualDiskAccessGetInfo VirtualDiskAccessMask = 0x00080000
VirtualDiskAccessCreate VirtualDiskAccessMask = 0x00100000
VirtualDiskAccessMetaOps VirtualDiskAccessMask = 0x00200000
VirtualDiskAccessRead VirtualDiskAccessMask = 0x000d0000
VirtualDiskAccessAll VirtualDiskAccessMask = 0x003f0000
VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000
// Flags for creating a VHD
CreateVirtualDiskFlagNone CreateVirtualDiskFlag = 0x0
CreateVirtualDiskFlagFullPhysicalAllocation CreateVirtualDiskFlag = 0x1
CreateVirtualDiskFlagPreventWritesToSourceDisk CreateVirtualDiskFlag = 0x2
CreateVirtualDiskFlagDoNotCopyMetadataFromParent CreateVirtualDiskFlag = 0x4
CreateVirtualDiskFlagCreateBackingStorage CreateVirtualDiskFlag = 0x8
CreateVirtualDiskFlagUseChangeTrackingSourceLimit CreateVirtualDiskFlag = 0x10
CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20
CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40
CreateVirtualDiskFlagSparseFile CreateVirtualDiskFlag = 0x80
CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100
CreateVirtualDiskFlagSupportCompressedVolumes CreateVirtualDiskFlag = 0x200
// Flags for opening a VHD
OpenVirtualDiskFlagNone VirtualDiskFlag = 0x00000000
OpenVirtualDiskFlagNoParents VirtualDiskFlag = 0x00000001
OpenVirtualDiskFlagBlankFile VirtualDiskFlag = 0x00000002
OpenVirtualDiskFlagBootDrive VirtualDiskFlag = 0x00000004
OpenVirtualDiskFlagCachedIO VirtualDiskFlag = 0x00000008
OpenVirtualDiskFlagCustomDiffChain VirtualDiskFlag = 0x00000010
OpenVirtualDiskFlagParentCachedIO VirtualDiskFlag = 0x00000020
OpenVirtualDiskFlagVhdsetFileOnly VirtualDiskFlag = 0x00000040
OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x00000080
OpenVirtualDiskFlagNoWriteHardening VirtualDiskFlag = 0x00000100
OpenVirtualDiskFlagSupportCompressedVolumes VirtualDiskFlag = 0x00000200
// Flags for attaching a VHD
AttachVirtualDiskFlagNone AttachVirtualDiskFlag = 0x00000000
AttachVirtualDiskFlagReadOnly AttachVirtualDiskFlag = 0x00000001
AttachVirtualDiskFlagNoDriveLetter AttachVirtualDiskFlag = 0x00000002
AttachVirtualDiskFlagPermanentLifetime AttachVirtualDiskFlag = 0x00000004
AttachVirtualDiskFlagNoLocalHost AttachVirtualDiskFlag = 0x00000008
AttachVirtualDiskFlagNoSecurityDescriptor AttachVirtualDiskFlag = 0x00000010
AttachVirtualDiskFlagBypassDefaultEncryptionPolicy AttachVirtualDiskFlag = 0x00000020
AttachVirtualDiskFlagNonPnp AttachVirtualDiskFlag = 0x00000040
AttachVirtualDiskFlagRestrictedRange AttachVirtualDiskFlag = 0x00000080
AttachVirtualDiskFlagSinglePartition AttachVirtualDiskFlag = 0x00000100
AttachVirtualDiskFlagRegisterVolume AttachVirtualDiskFlag = 0x00000200
// Flags for detaching a VHD
DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0
)
// CreateVhdx is a helper function to create a simple vhdx file at the given path using
// default values.
func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
params := CreateVirtualDiskParameters{
Version: 2,
Version2: CreateVersion2{
MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024,
BlockSizeInBytes: blockSizeInMb * 1024 * 1024,
},
}
handle, err := CreateVirtualDisk(path, VirtualDiskAccessNone, CreateVirtualDiskFlagNone, &params)
if err != nil {
return err
}
if err := syscall.CloseHandle(handle); err != nil {
return err
}
return nil
}
// DetachVirtualDisk detaches a virtual hard disk by handle.
func DetachVirtualDisk(handle syscall.Handle) (err error) {
if err := detachVirtualDisk(handle, 0, 0); err != nil {
return errors.Wrap(err, "failed to detach virtual disk")
}
return nil
}
// DetachVhd detaches a vhd found at `path`.
func DetachVhd(path string) error {
handle, err := OpenVirtualDisk(
path,
VirtualDiskAccessNone,
OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator,
)
if err != nil {
return err
}
defer syscall.CloseHandle(handle)
return DetachVirtualDisk(handle)
}
// AttachVirtualDisk attaches a virtual hard disk for use.
func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtualDiskFlag, parameters *AttachVirtualDiskParameters) (err error) {
// Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5.
if err := attachVirtualDisk(
handle,
nil,
uint32(attachVirtualDiskFlag),
0,
parameters,
nil,
); err != nil {
return errors.Wrap(err, "failed to attach virtual disk")
}
return nil
}
// AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2
// of the ATTACH_VIRTUAL_DISK_PARAMETERS.
func AttachVhd(path string) (err error) {
handle, err := OpenVirtualDisk(
path,
VirtualDiskAccessNone,
OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator,
)
if err != nil {
return err
}
defer syscall.CloseHandle(handle)
params := AttachVirtualDiskParameters{Version: 2}
if err := AttachVirtualDisk(
handle,
AttachVirtualDiskFlagNone,
&params,
); err != nil {
return errors.Wrap(err, "failed to attach virtual disk")
}
return nil
}
// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag) (syscall.Handle, error) {
parameters := OpenVirtualDiskParameters{Version: 2}
handle, err := OpenVirtualDiskWithParameters(
vhdPath,
virtualDiskAccessMask,
openVirtualDiskFlags,
&parameters,
)
if err != nil {
return 0, err
}
return handle, nil
}
// OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters.
func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag, parameters *OpenVirtualDiskParameters) (syscall.Handle, error) {
var (
handle syscall.Handle
defaultType VirtualStorageType
)
if parameters.Version != 2 {
return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version)
}
if err := openVirtualDisk(
&defaultType,
vhdPath,
uint32(virtualDiskAccessMask),
uint32(openVirtualDiskFlags),
parameters,
&handle,
); err != nil {
return 0, errors.Wrap(err, "failed to open virtual disk")
}
return handle, nil
}
// CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk.
func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, createVirtualDiskFlags CreateVirtualDiskFlag, parameters *CreateVirtualDiskParameters) (syscall.Handle, error) {
var (
handle syscall.Handle
defaultType VirtualStorageType
)
if parameters.Version != 2 {
return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version)
}
if err := createVirtualDisk(
&defaultType,
path,
uint32(virtualDiskAccessMask),
nil,
uint32(createVirtualDiskFlags),
0,
parameters,
nil,
&handle,
); err != nil {
return handle, errors.Wrap(err, "failed to create virtual disk")
}
return handle, nil
}
// GetVirtualDiskPhysicalPath takes a handle to a virtual hard disk and returns the physical
// path of the disk on the machine. This path is in the form \\.\PhysicalDriveX where X is an integer
// that represents the particular enumeration of the physical disk on the caller's system.
func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) {
var (
diskPathSizeInBytes uint32 = 256 * 2 // max path length 256 wide chars
diskPhysicalPathBuf [256]uint16
)
if err := getVirtualDiskPhysicalPath(
handle,
&diskPathSizeInBytes,
&diskPhysicalPathBuf[0],
); err != nil {
return "", errors.Wrap(err, "failed to get disk physical path")
}
return windows.UTF16ToString(diskPhysicalPathBuf[:]), nil
}
// CreateDiffVhd is a helper function to create a differencing virtual disk.
func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error {
// Setting `ParentPath` is how to signal to create a differencing disk.
createParams := &CreateVirtualDiskParameters{
Version: 2,
Version2: CreateVersion2{
ParentPath: windows.StringToUTF16Ptr(baseVhdPath),
BlockSizeInBytes: blockSizeInMB * 1024 * 1024,
OpenFlags: uint32(OpenVirtualDiskFlagCachedIO),
},
}
vhdHandle, err := CreateVirtualDisk(
diffVhdPath,
VirtualDiskAccessNone,
CreateVirtualDiskFlagNone,
createParams,
)
if err != nil {
return fmt.Errorf("failed to create differencing vhd: %s", err)
}
if err := syscall.CloseHandle(vhdHandle); err != nil {
return fmt.Errorf("failed to close differencing vhd handle: %s", err)
}
return nil
}

View File

@ -1,106 +0,0 @@
// Code generated by 'go generate'; DO NOT EDIT.
package vhd
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modvirtdisk = windows.NewLazySystemDLL("virtdisk.dll")
procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk")
procCreateVirtualDisk = modvirtdisk.NewProc("CreateVirtualDisk")
procDetachVirtualDisk = modvirtdisk.NewProc("DetachVirtualDisk")
procGetVirtualDiskPhysicalPath = modvirtdisk.NewProc("GetVirtualDiskPhysicalPath")
procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk")
)
func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(attachVirtualDiskFlag), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)))
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(path)
if err != nil {
return
}
return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, createVirtualDiskFlags, providerSpecificFlags, parameters, overlapped, handle)
}
func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) {
r1, _, e1 := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(createVirtualDiskFlags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(handle)))
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (err error) {
r1, _, e1 := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(detachVirtualDiskFlags), uintptr(providerSpecificFlags))
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (err error) {
r1, _, e1 := syscall.Syscall(procGetVirtualDiskPhysicalPath.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer)))
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(path)
if err != nil {
return
}
return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, openVirtualDiskFlags, parameters, handle)
}
func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) {
r1, _, e1 := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle)))
if r1 != 0 {
err = errnoErr(e1)
}
return
}

View File

@ -1,427 +0,0 @@
// Code generated by 'go generate'; DO NOT EDIT.
package winio
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modntdll = windows.NewLazySystemDLL("ntdll.dll")
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
procBackupRead = modkernel32.NewProc("BackupRead")
procBackupWrite = modkernel32.NewProc("BackupWrite")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
procCreateFileW = modkernel32.NewProc("CreateFileW")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
procLocalFree = modkernel32.NewProc("LocalFree")
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
procbind = modws2_32.NewProc("bind")
)
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
var _p0 uint32
if releaseAll {
_p0 = 1
}
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
success = r0 != 0
if true {
err = errnoErr(e1)
}
return
}
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(str)
if err != nil {
return
}
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
}
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
len = uint32(r0)
return
}
func impersonateSelf(level uint32) (err error) {
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(accountName)
if err != nil {
return
}
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
}
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(systemName)
if err != nil {
return
}
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
}
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(systemName)
if err != nil {
return
}
return _lookupPrivilegeName(_p0, luid, buffer, size)
}
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(systemName)
if err != nil {
return
}
var _p1 *uint16
_p1, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _lookupPrivilegeValue(_p0, _p1, luid)
}
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
var _p0 uint32
if openAsSelf {
_p0 = 1
}
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func revertToSelf() (err error) {
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
var _p0 *byte
if len(b) > 0 {
_p0 = &b[0]
}
var _p1 uint32
if abort {
_p1 = 1
}
var _p2 uint32
if processSecurity {
_p2 = 1
}
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
var _p0 *byte
if len(b) > 0 {
_p0 = &b[0]
}
var _p1 uint32
if abort {
_p1 = 1
}
var _p2 uint32
if processSecurity {
_p2 = 1
}
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
}
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
err = errnoErr(e1)
}
return
}
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
newport = syscall.Handle(r0)
if newport == 0 {
err = errnoErr(e1)
}
return
}
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
}
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
err = errnoErr(e1)
}
return
}
func getCurrentThread() (h syscall.Handle) {
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
h = syscall.Handle(r0)
return
}
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
ptr = uintptr(r0)
return
}
func localFree(mem uintptr) {
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
return
}
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
status = ntstatus(r0)
return
}
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
status = ntstatus(r0)
return
}
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
status = ntstatus(r0)
return
}
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
if r0 != 0 {
winerr = syscall.Errno(r0)
}
return
}
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
var _p0 uint32
if wait {
_p0 = 1
}
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
if r1 == socketError {
err = errnoErr(e1)
}
return
}

View File

@ -1 +0,0 @@
* text=auto eol=lf

View File

@ -1 +0,0 @@
*.exe

View File

@ -1,17 +0,0 @@
{
"Vendor": true,
"Deadline": "2m",
"Sort": [
"linter",
"severity",
"path",
"line"
],
"Skip": [
"internal\\schema2"
],
"EnableGC": true,
"Enable": [
"gofmt"
]
}

View File

@ -1,3 +0,0 @@
* @microsoft/containerplat
/hcn/* @nagiesek

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,54 +0,0 @@
version = "unstable"
generator = "gogoctrd"
plugins = ["grpc", "fieldpath"]
# Control protoc include paths. Below are usually some good defaults, but feel
# free to try it without them if it works for your project.
[includes]
# Include paths that will be added before all others. Typically, you want to
# treat the root of the project as an include, but this may not be necessary.
before = ["./protobuf"]
# Paths that should be treated as include roots in relation to the vendor
# directory. These will be calculated with the vendor directory nearest the
# target package.
packages = ["github.com/gogo/protobuf"]
# Paths that will be added untouched to the end of the includes. We use
# `/usr/local/include` to pickup the common install location of protobuf.
# This is the default.
after = ["/usr/local/include"]
# This section maps protobuf imports to Go packages. These will become
# `-M` directives in the call to the go protobuf generator.
[packages]
"gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto"
"google/protobuf/any.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/empty.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
"google/protobuf/field_mask.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/duration.proto" = "github.com/gogo/protobuf/types"
"github/containerd/cgroups/stats/v1/metrics.proto" = "github.com/containerd/cgroups/stats/v1"
[[overrides]]
prefixes = ["github.com/Microsoft/hcsshim/internal/shimdiag"]
plugins = ["ttrpc"]
# Lock down runhcs config
[[descriptors]]
prefix = "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
target = "cmd/containerd-shim-runhcs-v1/options/next.pb.txt"
ignore_files = [
"google/protobuf/descriptor.proto",
"gogoproto/gogo.proto"
]
[[descriptors]]
prefix = "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
target = "cmd/containerd-shim-runhcs-v1/stats/next.pb.txt"
ignore_files = [
"google/protobuf/descriptor.proto",
"gogoproto/gogo.proto"
]

View File

@ -1,46 +0,0 @@
# hcsshim
[![Build status](https://ci.appveyor.com/api/projects/status/nbcw28mnkqml0loa/branch/master?svg=true)](https://ci.appveyor.com/project/WindowsVirtualization/hcsshim/branch/master)
This package contains the Golang interface for using the Windows [Host Compute Service](https://techcommunity.microsoft.com/t5/containers/introducing-the-host-compute-service-hcs/ba-p/382332) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS).
It is primarily used in the [Moby Project](https://github.com/moby/moby), but it can be freely used by other projects as well.
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
We also ask that contributors [sign their commits](https://git-scm.com/docs/git-commit) using `git commit -s` or `git commit --signoff` to certify they either authored the work themselves or otherwise have permission to use it in this project.
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Dependencies
This project requires Golang 1.9 or newer to build.
For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements).
## Reporting Security Issues
Security issues and bugs should be reported privately, via email, to the Microsoft Security
Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should
receive a response within 24 hours. If for some reason you do not, please follow up via
email to ensure we received your original message. Further information, including the
[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in
the [Security TechCenter](https://technet.microsoft.com/en-us/security/default).
For additional details, see [Report a Computer Security Vulnerability](https://technet.microsoft.com/en-us/security/ff852094.aspx) on Technet
---------------
Copyright (c) 2018 Microsoft Corp. All rights reserved.

View File

@ -1,45 +0,0 @@
version: 0.1.{build}
image: Visual Studio 2017
clone_folder: c:\gopath\src\github.com\Microsoft\hcsshim
environment:
GOPATH: c:\gopath
PATH: "%GOPATH%\\bin;C:\\gometalinter-2.0.12-windows-amd64;%PATH%"
stack: go 1.13.4
build_script:
- appveyor DownloadFile https://github.com/alecthomas/gometalinter/releases/download/v2.0.12/gometalinter-2.0.12-windows-amd64.zip
- 7z x gometalinter-2.0.12-windows-amd64.zip -y -oC:\ > NUL
- gometalinter.exe --config .gometalinter.json ./...
- go build ./cmd/containerd-shim-runhcs-v1
- go build ./cmd/runhcs
- go build ./cmd/tar2ext4
- go build ./cmd/wclayer
- go build ./cmd/device-util
- go build ./internal/tools/grantvmgroupaccess
- go build ./internal/tools/uvmboot
- go build ./internal/tools/zapdir
- go test -v ./... -tags admin
- cd test
- go test -v ./internal -tags admin
- go test -c ./containerd-shim-runhcs-v1/ -tags functional
- go test -c ./cri-containerd/ -tags functional
- go test -c ./functional/ -tags functional
- go test -c ./runhcs/ -tags functional
artifacts:
- path: 'containerd-shim-runhcs-v1.exe'
- path: 'runhcs.exe'
- path: 'tar2ext4.exe'
- path: 'device-util.exe'
- path: 'wclayer.exe'
- path: 'grantvmgroupaccess.exe'
- path: 'uvmboot.exe'
- path: 'zapdir.exe'
- path: './test/containerd-shim-runhcs-v1.test.exe'
- path: './test/cri-containerd.test.exe'
- path: './test/functional.test.exe'
- path: './test/runhcs.test.exe'

View File

@ -1 +0,0 @@
package options

View File

@ -1,221 +0,0 @@
file {
name: "google/protobuf/timestamp.proto"
package: "google.protobuf"
message_type {
name: "Timestamp"
field {
name: "seconds"
number: 1
label: LABEL_OPTIONAL
type: TYPE_INT64
json_name: "seconds"
}
field {
name: "nanos"
number: 2
label: LABEL_OPTIONAL
type: TYPE_INT32
json_name: "nanos"
}
}
options {
java_package: "com.google.protobuf"
java_outer_classname: "TimestampProto"
java_multiple_files: true
go_package: "github.com/golang/protobuf/ptypes/timestamp"
cc_enable_arenas: true
objc_class_prefix: "GPB"
csharp_namespace: "Google.Protobuf.WellKnownTypes"
}
syntax: "proto3"
}
file {
name: "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto"
package: "containerd.runhcs.v1"
dependency: "gogoproto/gogo.proto"
dependency: "google/protobuf/timestamp.proto"
message_type {
name: "Options"
field {
name: "debug"
number: 1
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "debug"
}
field {
name: "debug_type"
number: 2
label: LABEL_OPTIONAL
type: TYPE_ENUM
type_name: ".containerd.runhcs.v1.Options.DebugType"
json_name: "debugType"
}
field {
name: "registry_root"
number: 3
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "registryRoot"
}
field {
name: "sandbox_image"
number: 4
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "sandboxImage"
}
field {
name: "sandbox_platform"
number: 5
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "sandboxPlatform"
}
field {
name: "sandbox_isolation"
number: 6
label: LABEL_OPTIONAL
type: TYPE_ENUM
type_name: ".containerd.runhcs.v1.Options.SandboxIsolation"
json_name: "sandboxIsolation"
}
field {
name: "boot_files_root_path"
number: 7
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "bootFilesRootPath"
}
field {
name: "vm_processor_count"
number: 8
label: LABEL_OPTIONAL
type: TYPE_INT32
json_name: "vmProcessorCount"
}
field {
name: "vm_memory_size_in_mb"
number: 9
label: LABEL_OPTIONAL
type: TYPE_INT32
json_name: "vmMemorySizeInMb"
}
field {
name: "GPUVHDPath"
number: 10
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "GPUVHDPath"
}
field {
name: "scale_cpu_limits_to_sandbox"
number: 11
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "scaleCpuLimitsToSandbox"
}
enum_type {
name: "DebugType"
value {
name: "NPIPE"
number: 0
}
value {
name: "FILE"
number: 1
}
value {
name: "ETW"
number: 2
}
}
enum_type {
name: "SandboxIsolation"
value {
name: "PROCESS"
number: 0
}
value {
name: "HYPERVISOR"
number: 1
}
}
}
message_type {
name: "ProcessDetails"
field {
name: "image_name"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "imageName"
}
field {
name: "created_at"
number: 2
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: ".google.protobuf.Timestamp"
options {
65001: 0
65010: 1
}
json_name: "createdAt"
}
field {
name: "kernel_time_100_ns"
number: 3
label: LABEL_OPTIONAL
type: TYPE_UINT64
json_name: "kernelTime100Ns"
}
field {
name: "memory_commit_bytes"
number: 4
label: LABEL_OPTIONAL
type: TYPE_UINT64
json_name: "memoryCommitBytes"
}
field {
name: "memory_working_set_private_bytes"
number: 5
label: LABEL_OPTIONAL
type: TYPE_UINT64
json_name: "memoryWorkingSetPrivateBytes"
}
field {
name: "memory_working_set_shared_bytes"
number: 6
label: LABEL_OPTIONAL
type: TYPE_UINT64
json_name: "memoryWorkingSetSharedBytes"
}
field {
name: "process_id"
number: 7
label: LABEL_OPTIONAL
type: TYPE_UINT32
json_name: "processId"
}
field {
name: "user_time_100_ns"
number: 8
label: LABEL_OPTIONAL
type: TYPE_UINT64
json_name: "userTime100Ns"
}
field {
name: "exec_id"
number: 9
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "execId"
}
}
options {
go_package: "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options;options"
}
weak_dependency: 0
syntax: "proto3"
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More