vendor: github.com/moby/buildkit v0.23.0-rc1

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Jonathan A. Sternberg
2025-06-11 16:55:49 -05:00
committed by Sebastiaan van Stijn
parent a057f98a30
commit e4e7fcf668
227 changed files with 6302 additions and 8581 deletions

View File

@@ -16,7 +16,7 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c
github.com/Graylog2/go-gelf v0.0.0-20191017102106-1550ee647df0
github.com/Microsoft/go-winio v0.6.2
github.com/Microsoft/hcsshim v0.12.9
github.com/Microsoft/hcsshim v0.13.0
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
github.com/aws/aws-sdk-go-v2 v1.30.3
github.com/aws/aws-sdk-go-v2/config v1.27.27
@@ -27,7 +27,7 @@ require (
github.com/cloudflare/cfssl v1.6.4
github.com/containerd/cgroups/v3 v3.0.5
github.com/containerd/containerd/api v1.9.0
github.com/containerd/containerd/v2 v2.0.5
github.com/containerd/containerd/v2 v2.1.1
github.com/containerd/continuity v0.4.5
github.com/containerd/errdefs v1.0.0
github.com/containerd/errdefs/pkg v0.3.0
@@ -62,7 +62,7 @@ require (
github.com/miekg/dns v1.1.66
github.com/mistifyio/go-zfs/v3 v3.0.1
github.com/mitchellh/copystructure v1.2.0
github.com/moby/buildkit v0.22.0
github.com/moby/buildkit v0.23.0-rc1
github.com/moby/docker-image-spec v1.3.1
github.com/moby/go-archive v0.1.0
github.com/moby/ipvs v1.1.0
@@ -122,7 +122,6 @@ require (
require (
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go/longrunning v0.5.5 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0 // indirect
@@ -154,8 +153,8 @@ require (
github.com/containerd/plugin v1.0.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containernetworking/cni v1.2.3 // indirect
github.com/containernetworking/plugins v1.6.2 // indirect
github.com/containernetworking/cni v1.3.0 // indirect
github.com/containernetworking/plugins v1.7.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
@@ -183,7 +182,7 @@ require (
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/in-toto/in-toto-golang v0.5.0 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmoiron/sqlx v1.3.3 // indirect
github.com/klauspost/compress v1.18.0 // indirect
@@ -202,16 +201,16 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/spdx/tools-golang v0.5.3 // indirect
github.com/spdx/tools-golang v0.5.5 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 // indirect
github.com/tonistiigi/go-actions-cache v0.0.0-20250228231703-3e9a6642607f // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f // indirect
github.com/tonistiigi/go-actions-cache v0.0.0-20250611155157-388a2ec8cdf8 // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b // indirect
@@ -238,7 +237,7 @@ require (
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/tools v0.32.0 // indirect
google.golang.org/api v0.160.0 // indirect
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect; TODO(thaJeztah): should we keep this one aligned with the other google.golang.org/genproto/xxx modules?
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect; TODO(thaJeztah): should we keep this one aligned with the other google.golang.org/genproto/xxx modules?
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect

View File

@@ -3,8 +3,8 @@ cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZw=
cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE=
cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg=
@@ -15,8 +15,6 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
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/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
@@ -37,8 +35,8 @@ github.com/Graylog2/go-gelf v0.0.0-20191017102106-1550ee647df0 h1:cOjLyhBhe91glg
github.com/Graylog2/go-gelf v0.0.0-20191017102106-1550ee647df0/go.mod h1:fBaQWrftOD5CrVCUfoYGHs4X4VViTuGOXA8WloCjTY0=
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/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
@@ -127,8 +125,8 @@ github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/q
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
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/containerd/v2 v2.1.1 h1:znnkm7Ajz8lg8BcIPMhc/9yjBRN3B+OkNKqKisKfwwM=
github.com/containerd/containerd/v2 v2.1.1/go.mod h1:zIfkQj4RIodclYQkX7GSSswSwgP8d/XxDOtOAoSDIGU=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -155,10 +153,10 @@ github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRq
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.6.2 h1:pqP8Mq923TLyef5g97XfJ/xpDeVek4yF8A4mzy9Tc4U=
github.com/containernetworking/plugins v1.6.2/go.mod h1:SP5UG3jDO9LtmfbBJdP+nl3A1atOtbj2MBOYsnaxy64=
github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo=
github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4=
github.com/containernetworking/plugins v1.7.1 h1:CNAR0jviDj6FS5Vg85NTgKWLDzZPfi/lj+VJfhMDTIs=
github.com/containernetworking/plugins v1.7.1/go.mod h1:xuMdjuio+a1oVQsHKjr/mgzuZ24leAsqUYRnzGoXHy0=
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.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -177,8 +175,8 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
@@ -265,11 +263,12 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
@@ -329,8 +328,8 @@ github.com/hashicorp/memberlist v0.4.0 h1:k3uda5gZcltmafuFF+UFqNEl5PrH+yPZ4zkjp1
github.com/hashicorp/memberlist v0.4.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
github.com/hashicorp/serf v0.8.5 h1:ZynDUIQiA8usmRgPdGPHFdPnb1wgGI9tK3mO9hcAJjc=
github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
github.com/in-toto/in-toto-golang v0.5.0 h1:hb8bgwr0M2hGdDsLjkJ3ZqJ8JFLL/tgYdAxF/XEFBbY=
github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1GdHMCq8+WPxw8/BE=
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 h1:i2fYnDurfLlJH8AyyMOnkLHnHeP8Ff/DDpuZA/D3bPo=
@@ -385,8 +384,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
github.com/moby/buildkit v0.22.0 h1:aWN06w1YGSVN1XfeZbj2ZbgY+zi5xDAjEFI8Cy9fTjA=
github.com/moby/buildkit v0.22.0/go.mod h1:j4pP5hxiTWcz7xuTK2cyxQislHl/N2WWHzOy43DlLJw=
github.com/moby/buildkit v0.23.0-rc1 h1:RIAEITsycLbXUt//rEPEfZUFnKUcm1cvpuWOfOidiWU=
github.com/moby/buildkit v0.23.0-rc1/go.mod h1:v5jMDvQgUyidk3wu3NvVAAd5JJo83nfet9Gf/o0+EAQ=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
@@ -436,10 +435,10 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y=
github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/cgroups v0.0.2 h1:A+mAPPMfgKNCEZUUtibESFx06uvhAmvo8sSz3Abwk7o=
github.com/opencontainers/cgroups v0.0.2/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs=
@@ -515,8 +514,8 @@ github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA=
github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -527,8 +526,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY=
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk=
github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
@@ -547,6 +546,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
@@ -557,14 +557,14 @@ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 h1:k9tdF32oJYwtjzMx+D26M6eYiCaAPdJ7tyN7tF1oU5Q=
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/go-actions-cache v0.0.0-20250228231703-3e9a6642607f h1:q/SWz3Bz0KtAsqaBo73CHVXjaz5O8PDnmD2JHVhgYnE=
github.com/tonistiigi/go-actions-cache v0.0.0-20250228231703-3e9a6642607f/go.mod h1:h0oRlVs3NoFIHysRQ4rU1+RG4QmU0M2JVSwTYrB4igk=
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI=
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/go-actions-cache v0.0.0-20250611155157-388a2ec8cdf8 h1:KbACff2cR+ip6/Kmt8zCzieMlKYyypArQT1ZweFxewQ=
github.com/tonistiigi/go-actions-cache v0.0.0-20250611155157-388a2ec8cdf8/go.mod h1:gJlfrsY8U+1n9RGKSgWryNFfzHRl/b1a99RUVL1L4Qw=
github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0=
github.com/tonistiigi/go-archvariant v1.0.0/go.mod h1:TxFmO5VS6vMq2kvs3ht04iPXtu2rUT/erOnGFYfk5Ho=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE=
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
@@ -645,6 +645,8 @@ go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
@@ -784,8 +786,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
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 v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
@@ -835,13 +837,12 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 h1:Th2b8jljYqkyZKS3aD3N9VpYsQpHuXLgea+SZUIfODA=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.73/go.mod h1:hbeKwKcboEsxARYmcy/AdPVN11wmT/Wnpgv4k4ftyqY=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 h1:SEAEUiPVylTD4vqqi+vtGkSnXeP2FcRO3FoZB1MklMw=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.73/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.76 h1:mrdLPj8ujM6eIKGtd1PkkuCIodpFFDM42Cfm0YODkIM=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.76/go.mod h1:7V2BQeHnVAQwhCnCPJ977giCeGDiywVewWF+8vkpPlc=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.76 h1:3DyzQ30OHt3wiOZVL1se2g1PAPJIU7+tMUyvfMUj1dY=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.76/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
resenje.org/singleflight v0.4.3 h1:l7foFYg8X/VEHPxWs1K/Pw77807RMVzvXgWGb0J1sdM=
resenje.org/singleflight v0.4.3/go.mod h1:lAgQK7VfjG6/pgredbQfmV0RvG/uVhKo6vSuZ0vCWfk=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,179 +0,0 @@
package testing
import (
"fmt"
fuzz "github.com/AdaLogics/go-fuzz-headers"
"os"
"reflect"
)
type F struct {
Data []byte
T *T
FuzzFunc func(*T, any)
}
func (f *F) CleanupTempDirs() {
f.T.CleanupTempDirs()
}
func (f *F) Add(args ...any) {}
func (c *F) Cleanup(f func()) {}
func (c *F) Error(args ...any) {}
func (c *F) Errorf(format string, args ...any) {}
func (f *F) Fail() {}
func (c *F) FailNow() {}
func (c *F) Failed() bool { return false }
func (c *F) Fatal(args ...any) {}
func (c *F) Fatalf(format string, args ...any) {}
func (f *F) Fuzz(ff any) {
// we are assuming that ff is a func.
// TODO: Add a check for UX purposes
fn := reflect.ValueOf(ff)
fnType := fn.Type()
var types []reflect.Type
for i := 1; i < fnType.NumIn(); i++ {
t := fnType.In(i)
types = append(types, t)
}
args := []reflect.Value{reflect.ValueOf(f.T)}
fuzzConsumer := fuzz.NewConsumer(f.Data)
for _, v := range types {
//fmt.Printf("arg %v\n", v)
newElem := reflect.New(v).Elem()
switch v.String() {
case "[]uint8":
b, err := fuzzConsumer.GetBytes()
if err != nil {
return
}
newElem.SetBytes(b)
case "string":
s, err := fuzzConsumer.GetString()
if err != nil {
return
}
newElem.SetString(s)
case "int":
randInt, err := fuzzConsumer.GetUint64()
if err != nil {
return
}
newElem.SetInt(int64(int(randInt)))
case "int8":
randInt, err := fuzzConsumer.GetByte()
if err != nil {
return
}
newElem.SetInt(int64(randInt))
case "int16":
randInt, err := fuzzConsumer.GetUint16()
if err != nil {
return
}
newElem.SetInt(int64(randInt))
case "int32":
randInt, err := fuzzConsumer.GetUint32()
if err != nil {
return
}
newElem.SetInt(int64(int32(randInt)))
case "int64":
randInt, err := fuzzConsumer.GetUint64()
if err != nil {
return
}
newElem.SetInt(int64(randInt))
case "uint":
randInt, err := fuzzConsumer.GetUint64()
if err != nil {
return
}
newElem.SetUint(uint64(uint(randInt)))
case "uint8":
randInt, err := fuzzConsumer.GetByte()
if err != nil {
return
}
newElem.SetUint(uint64(randInt))
case "uint16":
randInt, err := fuzzConsumer.GetUint16()
if err != nil {
return
}
newElem.SetUint(uint64(randInt))
case "uint32":
randInt, err := fuzzConsumer.GetUint32()
if err != nil {
return
}
newElem.SetUint(uint64(randInt))
case "uint64":
randInt, err := fuzzConsumer.GetUint64()
if err != nil {
return
}
newElem.SetUint(uint64(randInt))
case "rune":
randRune, err := fuzzConsumer.GetRune()
if err != nil {
return
}
newElem.Set(reflect.ValueOf(randRune))
case "float32":
randFloat, err := fuzzConsumer.GetFloat32()
if err != nil {
return
}
newElem.Set(reflect.ValueOf(randFloat))
case "float64":
randFloat, err := fuzzConsumer.GetFloat64()
if err != nil {
return
}
newElem.Set(reflect.ValueOf(randFloat))
case "bool":
randBool, err := fuzzConsumer.GetBool()
if err != nil {
return
}
newElem.Set(reflect.ValueOf(randBool))
default:
panic(fmt.Sprintf("unsupported type: %s", v.String()))
}
args = append(args, newElem)
}
fn.Call(args)
}
func (f *F) Helper() {}
func (c *F) Log(args ...any) {
fmt.Print(args...)
}
func (c *F) Logf(format string, args ...any) {
fmt.Println(fmt.Sprintf(format, args...))
}
func (c *F) Name() string { return "libFuzzer" }
func (c *F) Setenv(key, value string) {}
func (c *F) Skip(args ...any) {
panic("GO-FUZZ-BUILD-PANIC")
}
func (c *F) SkipNow() {
panic("GO-FUZZ-BUILD-PANIC")
}
func (c *F) Skipf(format string, args ...any) {
panic("GO-FUZZ-BUILD-PANIC")
}
func (f *F) Skipped() bool { return false }
func (f *F) TempDir() string {
dir, err := os.MkdirTemp("", "fuzzdir-")
if err != nil {
panic(err)
}
f.T.TempDirs = append(f.T.TempDirs, dir)
return dir
}

View File

@@ -1,129 +0,0 @@
package testing
import (
"fmt"
"os"
"strings"
"time"
)
// T can be used to terminate the current fuzz iteration
// without terminating the whole fuzz run. To do so, simply
// panic with the text "GO-FUZZ-BUILD-PANIC" and the fuzzer
// will recover.
type T struct {
TempDirs []string
}
func NewT() *T {
tempDirs := make([]string, 0)
return &T{TempDirs: tempDirs}
}
func unsupportedApi(name string) string {
plsOpenIss := "Please open an issue https://github.com/AdamKorcz/go-118-fuzz-build if you need this feature."
var b strings.Builder
b.WriteString(fmt.Sprintf("%s is not supported when fuzzing in libFuzzer mode\n.", name))
b.WriteString(plsOpenIss)
return b.String()
}
func (t *T) Cleanup(f func()) {
f()
}
func (t *T) Deadline() (deadline time.Time, ok bool) {
panic(unsupportedApi("t.Deadline()"))
}
func (t *T) Error(args ...any) {
fmt.Println(args...)
panic("error")
}
func (t *T) Errorf(format string, args ...any) {
fmt.Printf(format+"\n", args...)
panic("errorf")
}
func (t *T) Fail() {
panic("Called T.Fail()")
}
func (t *T) FailNow() {
panic("Called T.Fail()")
panic(unsupportedApi("t.FailNow()"))
}
func (t *T) Failed() bool {
panic(unsupportedApi("t.Failed()"))
}
func (t *T) Fatal(args ...any) {
fmt.Println(args...)
panic("fatal")
}
func (t *T) Fatalf(format string, args ...any) {
fmt.Printf(format+"\n", args...)
panic("fatal")
}
func (t *T) Helper() {
// We can't support it, but it also just impacts how failures are reported, so we can ignore it
}
func (t *T) Log(args ...any) {
fmt.Println(args...)
}
func (t *T) Logf(format string, args ...any) {
fmt.Println(format)
fmt.Println(args...)
}
func (t *T) Name() string {
return "libFuzzer"
}
func (t *T) Parallel() {
panic(unsupportedApi("t.Parallel()"))
}
func (t *T) Run(name string, f func(t *T)) bool {
panic(unsupportedApi("t.Run()"))
}
func (t *T) Setenv(key, value string) {
}
func (t *T) Skip(args ...any) {
panic("GO-FUZZ-BUILD-PANIC")
}
func (t *T) SkipNow() {
panic("GO-FUZZ-BUILD-PANIC")
}
// Is not really supported. We just skip instead
// of printing any message. A log message can be
// added if need be.
func (t *T) Skipf(format string, args ...any) {
panic("GO-FUZZ-BUILD-PANIC")
}
func (t *T) Skipped() bool {
panic(unsupportedApi("t.Skipped()"))
}
func (t *T) TempDir() string {
dir, err := os.MkdirTemp("", "fuzzdir-")
if err != nil {
panic(err)
}
t.TempDirs = append(t.TempDirs, dir)
return dir
}
func (t *T) CleanupTempDirs() {
if len(t.TempDirs) > 0 {
for _, tempDir := range t.TempDirs {
os.RemoveAll(tempDir)
}
}
}

View File

@@ -1,42 +0,0 @@
package testing
import (
"testing"
)
func AllocsPerRun(runs int, f func()) (avg float64) {
panic(unsupportedApi("testing.AllocsPerRun"))
}
func CoverMode() string {
panic(unsupportedApi("testing.CoverMode"))
}
func Coverage() float64 {
panic(unsupportedApi("testing.Coverage"))
}
func Init() {
panic(unsupportedApi("testing.Init"))
}
func RegisterCover(c testing.Cover) {
panic(unsupportedApi("testing.RegisterCover"))
}
func RunExamples(matchString func(pat, str string) (bool, error), examples []testing.InternalExample) (ok bool) {
panic(unsupportedApi("testing.RunExamples"))
}
func RunTests(matchString func(pat, str string) (bool, error), tests []testing.InternalTest) (ok bool) {
panic(unsupportedApi("testing.RunTests"))
}
func Short() bool {
return false
}
func Verbose() bool {
panic(unsupportedApi("testing.Verbose"))
}
type M struct {}
func (m *M) Run() (code int) {
panic("testing.M is not support in libFuzzer Mode")
}

12
vendor/github.com/Microsoft/hcsshim/.clang-format generated vendored Normal file
View File

@@ -0,0 +1,12 @@
Language: Cpp
BasedOnStyle: Microsoft
BreakBeforeBraces: Attach
PointerAlignment: Left
AllowShortFunctionsOnASingleLine: All
# match Go style
IndentCaseLabels: false
# don't break comments over line limit (needed for CodeQL exceptions)
ReflowComments: false
InsertNewlineAtEOF: true
KeepEmptyLines:
AtEndOfFile: true

View File

@@ -5,9 +5,6 @@ run:
- admin
- functional
- integration
skip-dirs:
# paths are relative to module root
- cri-containerd/test-images
linters:
enable:
@@ -34,13 +31,15 @@ linters-settings:
# struct order is often for Win32 compat
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
- fieldalignment
check-shadowing: true
stylecheck:
# https://staticcheck.io/docs/checks
checks: ["all"]
issues:
exclude-dirs:
# paths are relative to module root
- cri-containerd/test-images
exclude-rules:
# err is very often shadowed in nested scopes
- linters:
@@ -70,22 +69,22 @@ issues:
- path: layer.go
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: hcsshim.go
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: cmd\\ncproxy\\nodenetsvc\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: cmd\\ncproxy_mock\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\hcs\\schema2\\
linters:
@@ -95,67 +94,67 @@ issues:
- path: internal\\wclayer\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: hcn\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\hcs\\schema1\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\hns\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: ext4\\internal\\compactext4\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: ext4\\internal\\format\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\guestrequest\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\guest\\prot\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\windevice\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\winapi\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\vmcompute\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\regstate\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
- path: internal\\hcserror\\
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"
# v0 APIs are deprecated, but still retained for backwards compatability
- path: cmd\\ncproxy\\
@@ -171,4 +170,4 @@ issues:
- path: internal\\vhdx\\info
linters:
- stylecheck
Text: "ST1003:"
text: "ST1003:"

View File

@@ -1,13 +1,20 @@
BASE:=base.tar.gz
DEV_BUILD:=0
include Makefile.bootfiles
GO:=go
GO_FLAGS:=-ldflags "-s -w" # strip Go binaries
CGO_ENABLED:=0
GOMODVENDOR:=
KMOD:=0
CFLAGS:=-O2 -Wall
LDFLAGS:=-static -s # strip C binaries
LDFLAGS:=-static -s #strip C binaries
LDLIBS:=
PREPROCESSORFLAGS:=
ifeq "$(KMOD)" "1"
LDFLAGS:= -s
LDLIBS:= -lkmod
PREPROCESSORFLAGS:=-DMODULES=1
endif
GO_FLAGS_EXTRA:=
ifeq "$(GOMODVENDOR)" "1"
@@ -23,108 +30,14 @@ SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST))))
# additional directories to search for rule prerequisites and targets
VPATH=$(SRCROOT)
DELTA_TARGET=out/delta.tar.gz
ifeq "$(DEV_BUILD)" "1"
DELTA_TARGET=out/delta-dev.tar.gz
endif
ifeq "$(SNP_BUILD)" "1"
DELTA_TARGET=out/delta-snp.tar.gz
endif
# The link aliases for gcstools
GCS_TOOLS=\
generichook \
install-drivers
# Common path prefix.
PATH_PREFIX:=
# These have PATH_PREFIX prepended to obtain the full path in recipies e.g. $(PATH_PREFIX)/$(VMGS_TOOL)
VMGS_TOOL:=
IGVM_TOOL:=
KERNEL_PATH:=
.PHONY: all always rootfs test snp simple
.DEFAULT_GOAL := all
all: out/initrd.img out/rootfs.tar.gz
clean:
find -name '*.o' -print0 | xargs -0 -r rm
rm -rf bin deps rootfs out
test:
cd $(SRCROOT) && $(GO) test -v ./internal/guest/...
rootfs: out/rootfs.vhd
snp: out/kernelinitrd.vmgs out/rootfs.hash.vhd out/rootfs.vhd out/v2056.vmgs
simple: out/simple.vmgs snp
%.vmgs: %.bin
rm -f $@
# du -BM returns the size of the bin file in M, eg 7M. The sed command replaces the M with *1024*1024 and then bc does the math to convert to bytes
$(PATH_PREFIX)/$(VMGS_TOOL) create --filepath $@ --filesize `du -BM $< | sed "s/M.*/*1024*1024/" | bc`
$(PATH_PREFIX)/$(VMGS_TOOL) write --filepath $@ --datapath $< -i=8
# Simplest debug UVM used to test changes to the linux kernel. No dmverity protection. Boots an initramdisk rather than directly booting a vhd disk.
out/simple.bin: out/initrd.img $(PATH_PREFIX)/$(KERNEL_PATH) boot/startup_simple.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) -o $@ -kernel $(PATH_PREFIX)/$(KERNEL_PATH) -append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 rdinit=/startup_simple.sh" -rdinit out/initrd.img -vtl 0
ROOTFS_DEVICE:=/dev/sda
VERITY_DEVICE:=/dev/sdb
# Debug build for use with uvmtester. UVM with dm-verity protected vhd disk mounted directly via the kernel command line. Ignores corruption in dm-verity protected disk. (Use dmesg to see if dm-verity is ignoring data corruption.)
out/v2056.bin: out/rootfs.vhd out/rootfs.hash.vhd $(PATH_PREFIX)/$(KERNEL_PATH) out/rootfs.hash.datasectors out/rootfs.hash.datablocksize out/rootfs.hash.hashblocksize out/rootfs.hash.datablocks out/rootfs.hash.rootdigest out/rootfs.hash.salt boot/startup_v2056.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) -o $@ -kernel $(PATH_PREFIX)/$(KERNEL_PATH) -append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(VERITY_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) 0 sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt) 1 ignore_corruption\" init=/startup_v2056.sh" -vtl 0
# Full UVM with dm-verity protected vhd disk mounted directly via the kernel command line.
out/kernelinitrd.bin: out/rootfs.vhd out/rootfs.hash.vhd out/rootfs.hash.datasectors out/rootfs.hash.datablocksize out/rootfs.hash.hashblocksize out/rootfs.hash.datablocks out/rootfs.hash.rootdigest out/rootfs.hash.salt $(PATH_PREFIX)/$(KERNEL_PATH) boot/startup.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) -o $@ -kernel $(PATH_PREFIX)/$(KERNEL_PATH) -append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(VERITY_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) 0 sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt)\" init=/startup.sh" -vtl 0
# Rule to make a vhd from a file. This is used to create the rootfs.hash.vhd from rootfs.hash.
%.vhd: % bin/cmd/tar2ext4
./bin/cmd/tar2ext4 -only-vhd -i $< -o $@
# Rule to make a vhd from an ext4 file. This is used to create the rootfs.vhd from rootfs.ext4.
%.vhd: %.ext4 bin/cmd/tar2ext4
./bin/cmd/tar2ext4 -only-vhd -i $< -o $@
%.hash %.hash.info %.hash.datablocks %.hash.rootdigest %hash.datablocksize %.hash.datasectors %.hash.hashblocksize: %.ext4 %.hash.salt
veritysetup format --no-superblock --salt $(shell cat out/rootfs.hash.salt) $< $*.hash > $*.hash.info
# Retrieve info required by dm-verity at boot time
# Get the blocksize of rootfs
cat $*.hash.info | awk '/^Root hash:/{ print $$3 }' > $*.hash.rootdigest
cat $*.hash.info | awk '/^Salt:/{ print $$2 }' > $*.hash.salt
cat $*.hash.info | awk '/^Data block size:/{ print $$4 }' > $*.hash.datablocksize
cat $*.hash.info | awk '/^Hash block size:/{ print $$4 }' > $*.hash.hashblocksize
cat $*.hash.info | awk '/^Data blocks:/{ print $$3 }' > $*.hash.datablocks
echo $$(( $$(cat $*.hash.datablocks) * $$(cat $*.hash.datablocksize) / 512 )) > $*.hash.datasectors
out/rootfs.hash.salt:
hexdump -vn32 -e'8/4 "%08X" 1 "\n"' /dev/random > $@
out/rootfs.ext4: out/rootfs.tar.gz bin/cmd/tar2ext4
gzip -f -d ./out/rootfs.tar.gz
./bin/cmd/tar2ext4 -i ./out/rootfs.tar -o $@
out/rootfs.tar.gz: out/initrd.img
rm -rf rootfs-conv
mkdir rootfs-conv
gunzip -c out/initrd.img | (cd rootfs-conv && cpio -imd)
tar -zcf $@ -C rootfs-conv .
rm -rf rootfs-conv
out/initrd.img: $(BASE) $(DELTA_TARGET) $(SRCROOT)/hack/catcpio.sh
$(SRCROOT)/hack/catcpio.sh "$(BASE)" $(DELTA_TARGET) > out/initrd.img.uncompressed
gzip -c out/initrd.img.uncompressed > $@
rm out/initrd.img.uncompressed
# This target includes utilities which may be useful for testing purposes.
out/delta-dev.tar.gz: out/delta.tar.gz bin/internal/tools/snp-report
rm -rf rootfs-dev
@@ -168,10 +81,7 @@ out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/ho
tar -zcf $@ -C rootfs .
rm -rf rootfs
out/containerd-shim-runhcs-v1.exe:
GOOS=windows $(GO_BUILD) -o $@ $(SRCROOT)/cmd/containerd-shim-runhcs-v1
bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths bin/cmd/tar2ext4 bin/internal/tools/snp-report bin/cmd/dmverity-vhd:
bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths bin/cmd/tar2ext4 bin/internal/tools/snp-report:
@mkdir -p $(dir $@)
GOOS=linux $(GO_BUILD) -o $@ $(SRCROOT)/$(@:bin/%=%)
@@ -181,8 +91,8 @@ bin/vsockexec: vsockexec/vsockexec.o vsockexec/vsock.o
bin/init: init/init.o vsockexec/vsock.o
@mkdir -p bin
$(CC) $(LDFLAGS) -o $@ $^
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
%.o: %.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
$(CC) $(PREPROCESSORFLAGS) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<

197
vendor/github.com/Microsoft/hcsshim/Makefile.bootfiles generated vendored Normal file
View File

@@ -0,0 +1,197 @@
BASE:=base.tar.gz
DEV_BUILD:=0
DELTA_TARGET=out/delta.tar.gz
ifeq "$(DEV_BUILD)" "1"
DELTA_TARGET=out/delta-dev.tar.gz
endif
ifeq "$(SNP_BUILD)" "1"
DELTA_TARGET=out/delta-snp.tar.gz
endif
SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST))))
PATH_PREFIX:=
# These have PATH_PREFIX prepended to obtain the full path in recipies e.g. $(PATH_PREFIX)/$(VMGS_TOOL)
VMGS_TOOL:=
IGVM_TOOL:=
KERNEL_PATH:=
TAR2EXT4_TOOL:=bin/cmd/tar2ext4
ROOTFS_DEVICE:=/dev/sda
HASH_DEVICE:=/dev/sdb
.PHONY: all always rootfs test snp simple
.DEFAULT_GOAL := all
all: out/initrd.img out/rootfs.tar.gz
clean:
find -name '*.o' -print0 | xargs -0 -r rm
rm -rf bin rootfs out
rootfs: out/rootfs.vhd
snp: out/kernel.vmgs out/rootfs-verity.vhd out/v2056.vmgs out/v2056combined.vmgs
simple: out/simple.vmgs snp
%.vmgs: %.bin
rm -f $@
# du -BM returns the size of the bin file in M, eg 7M. The sed command replaces the M with *1024*1024 and then bc does the math to convert to bytes
$(PATH_PREFIX)/$(VMGS_TOOL) create --filepath $@ --filesize `du -BM $< | sed "s/M.*/*1024*1024/" | bc`
$(PATH_PREFIX)/$(VMGS_TOOL) write --filepath $@ --datapath $< -i=8
# Simplest debug UVM used to test changes to the linux kernel. No dmverity protection. Boots an initramdisk rather than directly booting a vhd disk.
out/simple.bin: out/initrd.img $(PATH_PREFIX)/$(KERNEL_PATH) boot/startup_simple.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) \
-o $@ \
-kernel $(PATH_PREFIX)/$(KERNEL_PATH) \
-append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 rdinit=/startup_simple.sh" \
-rdinit out/initrd.img \
-vtl 0
# The boot performance is optimized by supplying rootfs as a SCSI attachment. In this case the kernel boots with
# dm-verity to ensure the integrity. Similar to layer VHDs the verity Merkle tree is appended to ext4 filesystem.
# It transpires that the /dev/sd* order is not deterministic wrt the scsi device order. Thus build a single userland
# fs + merkle tree device and boot that.
#
# From https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/dm-init.html
#
# dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
#
# where:
# <name> ::= The device name.
# <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ""
# <minor> ::= The device minor number | ""
# <flags> ::= "ro" | "rw"
# <table> ::= <start_sector> <num_sectors> <target_type> <target_args>
# <target_type> ::= "verity" | "linear" | ... (see list below)
#
# From https://docs.kernel.org/admin-guide/device-mapper/verity.html
# <version> <dev> <hash_dev>
# <data_block_size> <hash_block_size>
# <num_data_blocks> <hash_start_block>
# <algorithm> <digest> <salt>
# [<#opt_params> <opt_params>]
#
# typical igvm tool line once all the macros are expanded
# python3 /home/user/igvmfile.py -o out/v2056.bin -kernel /hose/user/bzImage -append "8250_core.nr_uarts=0 panic=-1 debug loglevel=9 ignore_loglevel dev.scsi.logging_level=9411 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 196744 verity 1 /dev/sda /dev/sdb 4096 4096 24593 0 sha256 6d625a306aafdf73125a84388b7bfdd2c3a154bd8d698955f4adffc736bdfd66 b9065c23231f0d8901cc3a68e1d3b8d624213e76d6f9f6d3ccbcb829f9c710ba 1 ignore_corruption\" init=/startup_v2056.sh" -vtl 0
#
# so a kernel command line of:
# 8250_core.nr_uarts=0 panic=-1 debug loglevel=9 ignore_loglevel dev.scsi.logging_level=9411 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 196744 verity 1 /dev/sda /dev/sdb 4096 4096 24593 0 sha256 6d625a306aafdf73125a84388b7bfdd2c3a154bd8d698955f4adffc736bdfd66 b9065c23231f0d8901cc3a68e1d3b8d624213e76d6f9f6d3ccbcb829f9c710ba 1 ignore_corruption\" init=/startup_v2056.sh
#
# and a dm-mod.create of:
# dmverity,,,ro,0 196744 verity 1 /dev/sda /dev/sdb 4096 4096 24593 0 sha256 6d625a306aafdf73125a84388b7bfdd2c3a154bd8d698955f4adffc736bdfd66 b9065c23231f0d8901cc3a68e1d3b8d624213e76d6f9f6d3ccbcb829f9c710ba 1 ignore_corruption
#
# which breaks down to:
#
# name = "dmverity"
# uuid = ""
# minor = ""
# flags = "ro"
# table = 0 196744 verity "args"
# start_sector = 0
# num_sectors = 196744
# target_type = verity
# target_args = 1 /dev/sda /dev/sdb 4096 4096 24593 0 sha256 6d625a306aafdf73125a84388b7bfdd2c3a154bd8d698955f4adffc736bdfd66 b9065c23231f0d8901cc3a68e1d3b8d624213e76d6f9f6d3ccbcb829f9c710ba 1 ignore_corruption
# args:
# version 1
# dev /dev/sda
# hash_dev /dev/sdb
# data_block_size 4096
# hash_block_size 4096
# num_data_blocks 24593
# hash_start_block 0
# algorithm sha256
# digest 6d625a306aafdf73125a84388b7bfdd2c3a154bd8d698955f4adffc736bdfd66
# salt b9065c23231f0d8901cc3a68e1d3b8d624213e76d6f9f6d3ccbcb829f9c710ba
# opt_params
# count = 1
# ignore_corruption
#
# combined typical (not bigger count of sectors for the whole device)
# dmverity,,,ro,0 199672 verity 1 /dev/sda /dev/sda 4096 4096 24959 24959 sha256 4aa6e79866ee946ddbd9cddd6554bc6449272942fcc65934326817785a3bd374 adc4956274489c936395bab046a2d476f21ef436e571ba53da2fdf3aee59bf0a
#
# A few notes:
# - num_sectors is the size of the final (aka target) verity device, i.e. the size of our rootfs excluding the Merkle
# tree.
# - We don't add verity superblock, so the <hash_start_block> will be exactly at the end of ext4 filesystem and equal
# to its size. In the case when verity superblock is present an extra block should be added to the offset value,
# i.e. 24959 becomes 24960.
# Debug build for use with uvmtester. UVM with dm-verity protected vhd disk mounted directly via the kernel command line.
# Ignores corruption in dm-verity protected disk. (Use dmesg to see if dm-verity is ignoring data corruption.)
out/v2056.bin: out/rootfs.vhd out/rootfs.hash.vhd $(PATH_PREFIX)/$(KERNEL_PATH) out/rootfs.hash.datasectors out/rootfs.hash.datablocksize out/rootfs.hash.hashblocksize out/rootfs.hash.datablocks out/rootfs.hash.rootdigest out/rootfs.hash.salt boot/startup_v2056.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) \
-o $@ \
-kernel $(PATH_PREFIX)/$(KERNEL_PATH) \
-append "8250_core.nr_uarts=0 panic=-1 debug loglevel=9 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(HASH_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) $(shell cat out/rootfs.hash.datablocks) sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt) 1 ignore_corruption\" init=/startup_v2056.sh" \
-vtl 0
out/v2056combined.bin: out/rootfs-verity.vhd $(PATH_PREFIX)/$(KERNEL_PATH) out/rootfs.hash.datablocksize out/rootfs.hash.hashblocksize out/rootfs.hash.datablocks out/rootfs.hash.rootdigest out/rootfs.hash.salt boot/startup_v2056.sh
rm -f $@
echo root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(ROOTFS_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) $(shell cat out/rootfs.hash.datablocks) sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt) 1 ignore_corruption\"
python3 $(PATH_PREFIX)/$(IGVM_TOOL) \
-o $@ \
-kernel $(PATH_PREFIX)/$(KERNEL_PATH) \
-append "8250_core.nr_uarts=0 panic=-1 debug loglevel=9 ignore_loglevel dev.scsi.logging_level=9411 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(ROOTFS_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) $(shell cat out/rootfs.hash.datablocks) sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt) 1 ignore_corruption\" init=/startup_v2056.sh" \
-vtl 0
# Full UVM with dm-verity protected vhd disk mounted directly via the kernel command line.
out/kernel.bin: out/rootfs-verity.vhd $(PATH_PREFIX)/$(KERNEL_PATH) out/rootfs.hash.datasectors out/rootfs.hash.datablocksize out/rootfs.hash.hashblocksize out/rootfs.hash.datablocks out/rootfs.hash.rootdigest out/rootfs.hash.salt boot/startup.sh
rm -f $@
echo root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(ROOTFS_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) $(shell cat out/rootfs.hash.datablocks) sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt)\"
python3 $(PATH_PREFIX)/$(IGVM_TOOL) \
-o $@ \
-kernel $(PATH_PREFIX)/$(KERNEL_PATH) \
-append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(ROOTFS_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) $(shell cat out/rootfs.hash.datablocks) sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt)\" init=/startup.sh" \
-vtl 0
# Rule to make a vhd from a file. This is used to create the rootfs.hash.vhd from rootfs.hash.
%.vhd: % $(TAR2EXT4_TOOL)
$(TAR2EXT4_TOOL) -only-vhd -i $< -o $@
# Rule to make a vhd from an ext4 file. This is used to create the rootfs.vhd from rootfs.ext4.
%.vhd: %.ext4 $(TAR2EXT4_TOOL)
$(TAR2EXT4_TOOL) -only-vhd -i $< -o $@
%.hash %.hash.info %.hash.datablocks %.hash.rootdigest %hash.datablocksize %.hash.datasectors %.hash.hashblocksize: %.ext4 %.hash.salt
veritysetup format --no-superblock --salt $(shell cat out/rootfs.hash.salt) $< $*.hash > $*.hash.info
# Retrieve info required by dm-verity at boot time
# Get the blocksize of rootfs
cat $*.hash.info | awk '/^Root hash:/{ print $$3 }' > $*.hash.rootdigest
cat $*.hash.info | awk '/^Salt:/{ print $$2 }' > $*.hash.salt
cat $*.hash.info | awk '/^Data block size:/{ print $$4 }' > $*.hash.datablocksize
cat $*.hash.info | awk '/^Hash block size:/{ print $$4 }' > $*.hash.hashblocksize
cat $*.hash.info | awk '/^Data blocks:/{ print $$3 }' > $*.hash.datablocks
echo $$(( $$(cat $*.hash.datablocks) * $$(cat $*.hash.datablocksize) / 512 )) > $*.hash.datasectors
out/rootfs.hash.salt:
hexdump -vn32 -e'8/4 "%08X" 1 "\n"' /dev/random > $@
out/rootfs.ext4: out/rootfs.tar.gz $(TAR2EXT4_TOOL)
gzip -f -d ./out/rootfs.tar.gz
$(TAR2EXT4_TOOL) -i ./out/rootfs.tar -o $@
out/rootfs-verity.ext4: out/rootfs.ext4 out/rootfs.hash
cp out/rootfs.ext4 $@
cat out/rootfs.hash >> $@
out/rootfs.tar.gz: out/initrd.img
rm -rf rootfs-conv
mkdir rootfs-conv
gunzip -c out/initrd.img | (cd rootfs-conv && cpio -imd)
tar -zcf $@ -C rootfs-conv .
rm -rf rootfs-conv
out/initrd.img: $(BASE) $(DELTA_TARGET) $(SRCROOT)/hack/catcpio.sh
$(SRCROOT)/hack/catcpio.sh "$(BASE)" $(DELTA_TARGET) > out/initrd.img.uncompressed
gzip -c out/initrd.img.uncompressed > $@
rm out/initrd.img.uncompressed

View File

@@ -44,7 +44,7 @@ delta.tar.gz initrd.img rootfs.tar.gz
### Containerd Shim
For info on the [Runtime V2 API](https://github.com/containerd/containerd/blob/master/runtime/v2/README.md).
For info on the [Runtime V2 API](https://github.com/containerd/containerd/blob/main/core/runtime/v2/README.md).
Contrary to the typical Linux architecture of shim -> runc, the runhcs shim is used both to launch and manage the lifetime of containers.

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.2
// protoc-gen-go v1.35.2
// protoc v5.26.0
// source: github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto
@@ -195,11 +195,9 @@ type Options struct {
func (x *Options) Reset() {
*x = Options{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Options) String() string {
@@ -210,7 +208,7 @@ func (*Options) ProtoMessage() {}
func (x *Options) ProtoReflect() protoreflect.Message {
mi := &file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -385,11 +383,9 @@ type ProcessDetails struct {
func (x *ProcessDetails) Reset() {
*x = ProcessDetails{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ProcessDetails) String() string {
@@ -400,7 +396,7 @@ func (*ProcessDetails) ProtoMessage() {}
func (x *ProcessDetails) ProtoReflect() protoreflect.Message {
mi := &file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -643,32 +639,6 @@ func file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_run
if File_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*Options); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_github_com_Microsoft_hcsshim_cmd_containerd_shim_runhcs_v1_options_runhcs_proto_msgTypes[1].Exporter = func(v any, i int) any {
switch v := v.(*ProcessDetails); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{

View File

@@ -39,7 +39,7 @@ func checkForErrors(methodName string, hr error, resultBuffer *uint16) error {
if errorFound {
returnError := new(hr, methodName, result)
logrus.Debugf(returnError.Error()) // HCN errors logged for debugging.
logrus.Debug(returnError.Error()) // HCN errors logged for debugging.
return returnError
}

View File

@@ -63,10 +63,10 @@ func (process *Process) SystemID() string {
}
func (process *Process) processSignalResult(ctx context.Context, err error) (bool, error) {
switch err { //nolint:errorlint
case nil:
if err == nil {
return true, nil
case ErrVmcomputeOperationInvalidState, ErrComputeSystemDoesNotExist, ErrElementNotFound:
}
if errors.Is(err, ErrVmcomputeOperationInvalidState) || errors.Is(err, ErrComputeSystemDoesNotExist) || errors.Is(err, ErrElementNotFound) {
if !process.stopped() {
// The process should be gone, but we have not received the notification.
// After a second, force unblock the process wait to work around a possible
@@ -82,9 +82,8 @@ func (process *Process) processSignalResult(ctx context.Context, err error) (boo
}()
}
return false, nil
default:
return false, err
}
return false, nil
}
// Signal signals the process with `options`.

View File

@@ -24,4 +24,6 @@ type Chipset struct {
// LinuxKernelDirect - Added in v2.2 Builds >=181117
LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"`
FirmwareFile *FirmwareFile `json:"FirmwareFile,omitempty"`
}

View File

@@ -9,14 +9,6 @@
package hcsschema
const (
CimMountFlagNone uint32 = 0x0
CimMountFlagChildOnly uint32 = 0x1
CimMountFlagEnableDax uint32 = 0x2
CimMountFlagCacheFiles uint32 = 0x4
CimMountFlagCacheRegions uint32 = 0x8
)
type CimMount struct {
ImagePath string `json:"ImagePath,omitempty"`
FileSystemName string `json:"FileSystemName,omitempty"`

View File

@@ -0,0 +1,8 @@
package hcsschema
type FirmwareFile struct {
// Parameters is an experimental/pre-release field. The field itself or its
// behavior can change in future iterations of the schema. Avoid taking a hard
// dependency on this field.
Parameters []byte `json:"Parameters,omitempty"`
}

View File

@@ -1,49 +0,0 @@
/*
* HCS API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.1
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type Memory2 struct {
SizeInMB uint64 `json:"SizeInMB,omitempty"`
AllowOvercommit bool `json:"AllowOvercommit,omitempty"`
EnableHotHint bool `json:"EnableHotHint,omitempty"`
EnableColdHint bool `json:"EnableColdHint,omitempty"`
EnableEpf bool `json:"EnableEpf,omitempty"`
// EnableDeferredCommit is private in the schema. If regenerated need to add back.
EnableDeferredCommit bool `json:"EnableDeferredCommit,omitempty"`
// EnableColdDiscardHint if enabled, then the memory cold discard hint feature is exposed
// to the VM, allowing it to trim non-zeroed pages from the working set (if supported by
// the guest operating system).
EnableColdDiscardHint bool `json:"EnableColdDiscardHint,omitempty"`
// LowMmioGapInMB is the low MMIO region allocated below 4GB.
//
// TODO: This is pre-release support in schema 2.3. Need to add build number
// docs when a public build with this is out.
LowMMIOGapInMB uint64 `json:"LowMmioGapInMB,omitempty"`
// HighMmioBaseInMB is the high MMIO region allocated above 4GB (base and
// size).
//
// TODO: This is pre-release support in schema 2.3. Need to add build number
// docs when a public build with this is out.
HighMMIOBaseInMB uint64 `json:"HighMmioBaseInMB,omitempty"`
// HighMmioGapInMB is the high MMIO region.
//
// TODO: This is pre-release support in schema 2.3. Need to add build number
// docs when a public build with this is out.
HighMMIOGapInMB uint64 `json:"HighMmioGapInMB,omitempty"`
}

View File

@@ -0,0 +1,21 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swaggerapi/swaggercodegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swaggerapi/swaggercodegen.git)
*/
package hcsschema
type MemoryBackingType string
// List of MemoryBackingType
const (
MemoryBackingType_PHYSICAL MemoryBackingType = "Physical"
MemoryBackingType_VIRTUAL MemoryBackingType = "Virtual"
MemoryBackingType_HYBRID MemoryBackingType = "Hybrid"
)

View File

@@ -0,0 +1,19 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type Numa struct {
VirtualNodeCount uint8 `json:"VirtualNodeCount,omitempty"`
PreferredPhysicalNodes []int64 `json:"PreferredPhysicalNodes,omitempty"`
Settings []NumaSetting `json:"Settings,omitempty"`
MaxSizePerNode uint64 `json:"MaxSizePerNode,omitempty"`
}

View File

@@ -0,0 +1,17 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type NumaNode struct {
VirtualNodeIndex uint32 `json:"VirtualNodeIndex,omitempty"`
PhysicalNodeIndex uint32 `json:"PhysicalNodeIndex,omitempty"`
}

View File

@@ -0,0 +1,19 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type NumaNodeMemory struct {
// Total physical memory on on this physical NUMA node that is consumable by the VMs.
TotalConsumableMemoryInPages uint64 `json:"TotalConsumableMemoryInPages,omitempty"`
// Currently available physical memory on this physical NUMA node for the VMs.
AvailableMemoryInPages uint64 `json:"AvailableMemoryInPages,omitempty"`
}

View File

@@ -0,0 +1,17 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type NumaNodeProcessor struct {
TotalAssignedProcessors uint32 `json:"TotalAssignedProcessors,omitempty"`
TotalAvailableProcessors uint32 `json:"TotalAvailableProcessors,omitempty"`
}

View File

@@ -0,0 +1,21 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type NumaProcessors struct {
CountPerNode Range `json:"count_per_node,omitempty"`
NodePerSocket uint32 `json:"node_per_socket,omitempty"`
}
type Range struct {
Max uint32 `json:"max,omitempty"`
}

View File

@@ -0,0 +1,21 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type NumaSetting struct {
VirtualNodeNumber uint32 `json:"VirtualNodeNumber,omitempty"`
PhysicalNodeNumber uint32 `json:"PhysicalNodeNumber,omitempty"`
VirtualSocketNumber uint32 `json:"VirtualSocketNumber,omitempty"`
CountOfProcessors uint32 `json:"CountOfProcessors,omitempty"`
CountOfMemoryBlocks uint64 `json:"CountOfMemoryBlocks,omitempty"`
MemoryBackingType MemoryBackingType `json:"MemoryBackingType,omitempty"`
}

View File

@@ -1,23 +0,0 @@
/*
* HCS API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.5
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type Processor2 struct {
Count int32 `json:"Count,omitempty"`
Limit int32 `json:"Limit,omitempty"`
Weight int32 `json:"Weight,omitempty"`
ExposeVirtualizationExtensions bool `json:"ExposeVirtualizationExtensions,omitempty"`
// An optional object that configures the CPU Group to which a Virtual Machine is going to bind to.
CpuGroup *CpuGroup `json:"CpuGroup,omitempty"`
}

View File

@@ -26,6 +26,8 @@ type Properties struct {
RuntimeId string `json:"RuntimeId,omitempty"`
SystemGUID string `json:"SystemGUID,omitempty"`
RuntimeTemplateId string `json:"RuntimeTemplateId,omitempty"`
State string `json:"State,omitempty"`

View File

@@ -23,4 +23,5 @@ const (
PTICHeartbeatStatus PropertyType = "ICHeartbeatStatus"
PTProcessorTopology PropertyType = "ProcessorTopology"
PTCPUGroup PropertyType = "CpuGroup"
PTSystemGUID PropertyType = "SystemGUID"
)

View File

@@ -1,16 +1,18 @@
// Autogenerated code; DO NOT EDIT.
/*
* HCS API
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.1
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type Topology struct {
Memory *Memory2 `json:"Memory,omitempty"`
Processor *Processor2 `json:"Processor,omitempty"`
Memory *VirtualMachineMemory `json:"Memory,omitempty"`
Processor *VirtualMachineProcessor `json:"Processor,omitempty"`
Numa *Numa `json:"Numa,omitempty"`
}

View File

@@ -1,36 +1,29 @@
// Autogenerated code; DO NOT EDIT.
/*
* HCS API
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.1
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
// Configuration of a virtual machine, used during its creation to set up and/or use resources.
type VirtualMachine struct {
// StopOnReset is private in the schema. If regenerated need to put back.
StopOnReset bool `json:"StopOnReset,omitempty"`
Chipset *Chipset `json:"Chipset,omitempty"`
ComputeTopology *Topology `json:"ComputeTopology,omitempty"`
Devices *Devices `json:"Devices,omitempty"`
GuestState *GuestState `json:"GuestState,omitempty"`
RestoreState *RestoreState `json:"RestoreState,omitempty"`
Version *Version `json:"Version,omitempty"`
// When set to true, the virtual machine will treat a reset as a stop, releasing resources and cleaning up state.
StopOnReset bool `json:"StopOnReset,omitempty"`
Chipset *Chipset `json:"Chipset,omitempty"`
ComputeTopology *Topology `json:"ComputeTopology,omitempty"`
Devices *Devices `json:"Devices,omitempty"`
GuestState *GuestState `json:"GuestState,omitempty"`
RestoreState *RestoreState `json:"RestoreState,omitempty"`
RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"`
StorageQoS *StorageQoS `json:"StorageQoS,omitempty"`
StorageQoS *StorageQoS `json:"StorageQoS,omitempty"`
DebugOptions *DebugOptions `json:"DebugOptions,omitempty"`
GuestConnection *GuestConnection `json:"GuestConnection,omitempty"`
SecuritySettings *SecuritySettings `json:"SecuritySettings,omitempty"`
DebugOptions *DebugOptions `json:"DebugOptions,omitempty"`
SecuritySettings *SecuritySettings `json:"SecuritySettings,omitempty"`
}

View File

@@ -0,0 +1,33 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type VirtualMachineMemory struct {
SizeInMB uint64 `json:"SizeInMB,omitempty"`
Backing *MemoryBackingType `json:"Backing,omitempty"`
// If enabled, then the VM's memory is backed by the Windows pagefile rather than physically backed, statically allocated memory.
AllowOvercommit bool `json:"AllowOvercommit,omitempty"`
// If enabled, then the memory hot hint feature is exposed to the VM, allowing it to prefetch pages into its working set. (if supported by the guest operating system).
EnableHotHint bool `json:"EnableHotHint,omitempty"`
// If enabled, then the memory cold hint feature is exposed to the VM, allowing it to trim zeroed pages from its working set (if supported by the guest operating system).
EnableColdHint bool `json:"EnableColdHint,omitempty"`
// If enabled, then the memory cold discard hint feature is exposed to the VM, allowing it to trim non-zeroed pages from the working set (if supported by the guest operating system).
EnableColdDiscardHint bool `json:"EnableColdDiscardHint,omitempty"`
// If enabled, then commit is not charged for each backing page until first access.
EnableDeferredCommit bool `json:"EnableDeferredCommit,omitempty"`
// Low MMIO region allocated below 4GB
LowMMIOGapInMB uint64 `json:"LowMmioGapInMB,omitempty"`
// High MMIO region allocated above 4GB (base and size)
HighMMIOBaseInMB uint64 `json:"HighMmioBaseInMB,omitempty"`
HighMMIOGapInMB uint64 `json:"HighMmioGapInMB,omitempty"`
SlitType *VirtualSlitType `json:"SlitType,omitempty"`
}

View File

@@ -0,0 +1,21 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
type VirtualMachineProcessor struct {
Count uint32 `json:"Count,omitempty"`
Limit uint64 `json:"Limit,omitempty"`
Weight uint64 `json:"Weight,omitempty"`
Reservation uint64 `json:"Reservation,omitempty"`
CpuGroup *CpuGroup `json:"CpuGroup,omitempty"`
NumaProcessorsSettings *NumaProcessors `json:"NumaProcessorsSettings,omitempty"`
}

View File

@@ -9,8 +9,9 @@
package hcsschema
// TODO: This is pre-release support in schema 2.3. Need to add build number
// TODO: PropagateNumaAffinity is pre-release/experimental field in schema 2.11. Need to add build number
// docs when a public build with this is out.
type VirtualPciDevice struct {
Functions []VirtualPciFunction `json:",omitempty"`
PropagateNumaAffinity *bool `json:"PropagateNumaAffinity,omitempty"`
}

View File

@@ -0,0 +1,23 @@
// Autogenerated code; DO NOT EDIT.
/*
* Schema Open API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
// VirtualSlitType : Indicates if a virtual SLIT should ne enabled for a VM and the type of virtual SLIT to be enabled.
type VirtualSlitType string
// List of VirtualSlitType
const (
VirtualSlitType_NONE VirtualSlitType = "None"
VirtualSlitType_FIRMWARE VirtualSlitType = "Firmware"
VirtualSlitType_MEASURED VirtualSlitType = "Measured"
VirtualSlitType_FIRMWARE_FALLBACK_MEASURED VirtualSlitType = "FirmwareFallbackMeasured"
)

View File

@@ -13,4 +13,6 @@ type WindowsCrashReporting struct {
DumpFileName string `json:"DumpFileName,omitempty"`
MaxDumpSize int64 `json:"MaxDumpSize,omitempty"`
DumpType string `json:"DumpType,omitempty"`
}

View File

@@ -238,9 +238,10 @@ func (computeSystem *System) Shutdown(ctx context.Context) error {
resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "")
events := processHcsResult(ctx, resultJSON)
switch err { //nolint:errorlint
case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending:
default:
if err != nil &&
!errors.Is(err, ErrVmcomputeAlreadyStopped) &&
!errors.Is(err, ErrComputeSystemDoesNotExist) &&
!errors.Is(err, ErrVmcomputeOperationPending) {
return makeSystemError(computeSystem, operation, err, events)
}
return nil
@@ -259,9 +260,10 @@ func (computeSystem *System) Terminate(ctx context.Context) error {
resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "")
events := processHcsResult(ctx, resultJSON)
switch err { //nolint:errorlint
case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending:
default:
if err != nil &&
!errors.Is(err, ErrVmcomputeAlreadyStopped) &&
!errors.Is(err, ErrComputeSystemDoesNotExist) &&
!errors.Is(err, ErrVmcomputeOperationPending) {
return makeSystemError(computeSystem, operation, err, events)
}
return nil
@@ -279,14 +281,13 @@ func (computeSystem *System) waitBackground() {
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil)
switch err { //nolint:errorlint
case nil:
if err == nil {
log.G(ctx).Debug("system exited")
case ErrVmcomputeUnexpectedExit:
} else if errors.Is(err, ErrVmcomputeUnexpectedExit) {
log.G(ctx).Debug("unexpected system exit")
computeSystem.exitError = makeSystemError(computeSystem, operation, err, nil)
err = nil
default:
} else {
err = makeSystemError(computeSystem, operation, err, nil)
}
computeSystem.closedWaitOnce.Do(func() {

View File

@@ -47,7 +47,7 @@ func (nnvManagementMacList *HNSNnvManagementMacList) Set() (*HNSNnvManagementMac
func GetNnvManagementMacAddressList() (*HNSNnvManagementMacList, error) {
operation := "Get"
title := "hcsshim::nnvManagementMacList::" + operation
logrus.Debugf(title)
logrus.Debug(title)
return HNSNnvManagementMacRequest("GET", "", "")
}
@@ -55,6 +55,6 @@ func GetNnvManagementMacAddressList() (*HNSNnvManagementMacList, error) {
func DeleteNnvManagementMacAddressList() (*HNSNnvManagementMacList, error) {
operation := "Delete"
title := "hcsshim::nnvManagementMacList::" + operation
logrus.Debugf(title)
logrus.Debug(title)
return HNSNnvManagementMacRequest("DELETE", "", "")
}

View File

@@ -22,9 +22,8 @@ import (
// of the job and a mutex for synchronized handle access.
type JobObject struct {
handle windows.Handle
// All accesses to this MUST be done atomically except in `Open` as the object
// is being created in the function. 1 signifies that this job is currently a silo.
silo uint32
// silo signifies that this job is currently a silo.
silo atomic.Bool
mq *queue.MessageQueue
handleLock sync.RWMutex
}
@@ -204,9 +203,7 @@ func Open(ctx context.Context, options *Options) (_ *JobObject, err error) {
handle: jobHandle,
}
if isJobSilo(jobHandle) {
job.silo = 1
}
job.silo.Store(isJobSilo(jobHandle))
// If the IOCP we'll be using to receive messages for all jobs hasn't been
// created, create it and start polling.
@@ -479,7 +476,7 @@ func (job *JobObject) ApplyFileBinding(root, target string, readOnly bool) error
return ErrAlreadyClosed
}
if !job.isSilo() {
if !job.silo.Load() {
return ErrNotSilo
}
@@ -546,7 +543,7 @@ func (job *JobObject) PromoteToSilo() error {
return ErrAlreadyClosed
}
if job.isSilo() {
if job.silo.Load() {
return nil
}
@@ -569,15 +566,10 @@ func (job *JobObject) PromoteToSilo() error {
return fmt.Errorf("failed to promote job to silo: %w", err)
}
atomic.StoreUint32(&job.silo, 1)
job.silo.Store(true)
return nil
}
// isSilo returns if the job object is a silo.
func (job *JobObject) isSilo() bool {
return atomic.LoadUint32(&job.silo) == 1
}
// QueryPrivateWorkingSet returns the private working set size for the job. This is calculated by adding up the
// private working set for every process running in the job.
func (job *JobObject) QueryPrivateWorkingSet() (uint64, error) {

View File

@@ -150,6 +150,7 @@ func (job *JobObject) SetCPUAffinity(affinityBitMask uint64) error {
return fmt.Errorf("affinity bitmask (%d) exceeds max allowable value (%d)", affinityBitMask, maxUintptr)
}
// CodeQL [SM03681] checked against max value above (there is no math.MaxUintPtr ...)
info.BasicLimitInformation.Affinity = uintptr(affinityBitMask)
return job.setExtendedInformation(info)
}

View File

@@ -4,7 +4,6 @@ import (
"context"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
type entryContextKeyType int
@@ -20,13 +19,13 @@ var (
// Instead, use `L.With*` or `L.Dup()`. Or `G(context.Background())`.
L = logrus.NewEntry(logrus.StandardLogger())
// G is an alias for GetEntry
// G is an alias for GetEntry.
G = GetEntry
// S is an alias for SetEntry
// S is an alias for SetEntry.
S = SetEntry
// U is an alias for UpdateContext
// U is an alias for UpdateContext.
U = UpdateContext
)
@@ -83,7 +82,7 @@ func UpdateContext(ctx context.Context) context.Context {
// WithContext returns a context that contains the provided log entry.
// The entry can be extracted with `GetEntry` (`G`)
//
// The entry in the context is a copy of `entry` (generated by `entry.WithContext`)
// The entry in the context is a copy of `entry` (generated by `entry.WithContext`).
func WithContext(ctx context.Context, entry *logrus.Entry) (context.Context, *logrus.Entry) {
// regardless of the order, entry.Context != GetEntry(ctx)
// here, the returned entry will reference the supplied context
@@ -93,25 +92,6 @@ func WithContext(ctx context.Context, entry *logrus.Entry) (context.Context, *lo
return ctx, entry
}
// Copy extracts the tracing Span and logging entry from the src Context, if they
// exist, and adds them to the dst Context.
//
// This is useful to share tracing and logging between contexts, but not the
// cancellation. For example, if the src Context has been cancelled but cleanup
// operations triggered by the cancellation require a non-cancelled context to
// execute.
func Copy(dst context.Context, src context.Context) context.Context {
if s := trace.FromContext(src); s != nil {
dst = trace.NewContext(dst, s)
}
if e := fromContext(src); e != nil {
dst, _ = WithContext(dst, e)
}
return dst
}
func fromContext(ctx context.Context) *logrus.Entry {
e, _ := ctx.Value(_entryContextKey).(*logrus.Entry)
return e

View File

@@ -103,9 +103,7 @@ func encode(v interface{}) (_ []byte, err error) {
if jErr := enc.Encode(v); jErr != nil {
if err != nil {
// TODO (go1.20): use multierror via fmt.Errorf("...: %w; ...: %w", ...)
//nolint:errorlint // non-wrapping format verb for fmt.Errorf
return nil, fmt.Errorf("protojson encoding: %v; json encoding: %w", err, jErr)
return nil, fmt.Errorf("protojson encoding: %w; json encoding: %w", err, jErr)
}
return nil, fmt.Errorf("json encoding: %w", jErr)
}

View File

@@ -22,23 +22,14 @@ var (
// case sensitive keywords, so "env" is not a substring on "Environment"
_scrubKeywords = [][]byte{[]byte("env"), []byte("Environment")}
_scrub int32
_scrub atomic.Bool
)
// SetScrubbing enables scrubbing
func SetScrubbing(enable bool) {
v := int32(0) // cant convert from bool to int32 directly
if enable {
v = 1
}
atomic.StoreInt32(&_scrub, v)
}
func SetScrubbing(enable bool) { _scrub.Store(enable) }
// IsScrubbingEnabled checks if scrubbing is enabled
func IsScrubbingEnabled() bool {
v := atomic.LoadInt32(&_scrub)
return v != 0
}
func IsScrubbingEnabled() bool { return _scrub.Load() }
// ScrubProcessParameters scrubs HCS Create Process requests with config parameters of
// type internal/hcs/schema2.ScrubProcessParameters (aka hcsshema.ScrubProcessParameters)

View File

@@ -1,3 +0,0 @@
// vhdx package adds the utility methods necessary to deal with the vhdx that are used as the scratch
// space for the containers and the uvm.
package vhdx

View File

@@ -1,233 +0,0 @@
//go:build windows
package vhdx
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"os"
"syscall"
"unsafe"
"github.com/Microsoft/go-winio/pkg/guid"
"github.com/Microsoft/go-winio/vhd"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/oc"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
"golang.org/x/sys/windows"
)
const _IOCTL_DISK_GET_DRIVE_LAYOUT_EX = 0x00070050
var partitionBasicDataGUID = guid.GUID{
Data1: 0xebd0a0a2,
Data2: 0xb9e5,
Data3: 0x4433,
Data4: [8]byte{0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7},
}
const (
partitionStyleMBR uint32 = iota
partitionStyleGPT
partitionStyleRaw
)
// type partitionInformationMBR struct {
// PartitionType uint8
// BootIndicator uint8
// RecognizedPartition uint8
// HiddenSectors uint32
// PartitionId guid.GUID
// }
type partitionInformationGPT struct {
PartitionType guid.GUID
PartitionId guid.GUID
Attributes uint64
Name [72]byte // wide char
}
type partitionInformationEx struct {
PartitionStyle uint32
StartingOffset int64
PartitionLength int64
PartitionNumber uint32
RewritePartition uint8
IsServicePartition uint8
_ uint16
// A union of partitionInformationMBR and partitionInformationGPT
// since partitionInformationGPT is largest with 112 bytes
GptMbrUnion [112]byte
}
type driveLayoutInformationGPT struct {
DiskID guid.GUID
StartingUsableOffset int64
UsableLength int64
MaxPartitionCount uint32
}
// type driveLayoutInformationMBR struct {
// Signature uint32
// Checksum uint32
// }
type driveLayoutInformationEx struct {
PartitionStyle uint32
PartitionCount uint32
// A union of driveLayoutInformationGPT and driveLayoutInformationMBR
// since driveLayoutInformationGPT is largest with 40 bytes
GptMbrUnion [40]byte
PartitionEntry [1]partitionInformationEx
}
// Takes the physical path of a disk and retrieves the drive layout information of that disk. Returns the
// driveLayoutInformationEx struct and a slice of partitionInfomrationEx struct containing one element for
// each partition found on the vhdx. Note: some of the members like (GptMbrUnion) of these structs are raw
// byte arrays and it is the responsibility of the calling function to properly parse them.
func getDriveLayout(ctx context.Context, drivePhysicalPath string) (driveLayoutInformationEx, []partitionInformationEx, error) {
var (
outBytes uint32
err error
volume *os.File
)
layoutData := struct {
info driveLayoutInformationEx
// driveLayoutInformationEx has a flexible array member at the end. The data returned
// by IOCTL_DISK_GET_DRIVE_LAYOUT_EX usually has driveLayoutInformationEx.PartitionCount
// number of elements in this array. For all practical purposes we don't expect to have
// more than 64 partitions in a container/uvm vhdx.
partitions [63]partitionInformationEx
}{}
volume, err = os.OpenFile(drivePhysicalPath, os.O_RDONLY, 0)
if err != nil {
return layoutData.info, layoutData.partitions[:0], fmt.Errorf("failed to open drive: %w", err)
}
defer volume.Close()
err = windows.DeviceIoControl(windows.Handle(volume.Fd()),
_IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
nil,
0,
(*byte)(unsafe.Pointer(&layoutData)),
uint32(unsafe.Sizeof(layoutData)),
&outBytes,
nil)
if err != nil {
return layoutData.info, layoutData.partitions[:0], fmt.Errorf("IOCTL to get disk layout failed: %w", err)
}
if layoutData.info.PartitionCount == 0 {
return layoutData.info, []partitionInformationEx{}, nil
} else {
// parse the retrieved data into driveLayoutInformationEx and partitionInformationEx
partitions := make([]partitionInformationEx, layoutData.info.PartitionCount)
partitions[0] = layoutData.info.PartitionEntry[0]
copy(partitions[1:], layoutData.partitions[:layoutData.info.PartitionCount-1])
return layoutData.info, partitions, nil
}
}
// Scratch VHDs are formatted with GPT style and have 1 MSFT_RESERVED
// partition and 1 BASIC_DATA partition. This struct contains the
// partitionID of this BASIC_DATA partition and the DiskID of this
// scratch vhdx.
type ScratchVhdxPartitionInfo struct {
DiskID guid.GUID
PartitionID guid.GUID
}
// Returns the VhdxInfo of a GPT vhdx at path vhdxPath.
func GetScratchVhdPartitionInfo(ctx context.Context, vhdxPath string) (_ ScratchVhdxPartitionInfo, err error) {
var (
diskHandle syscall.Handle
driveLayout driveLayoutInformationEx
partitions []partitionInformationEx
gptDriveLayout driveLayoutInformationGPT
gptPartitionInfo partitionInformationGPT
volumePath string
)
title := "hcsshim::GetScratchVhdPartitionInfo"
ctx, span := trace.StartSpan(ctx, title)
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(
trace.StringAttribute("path", vhdxPath))
diskHandle, err = vhd.OpenVirtualDisk(vhdxPath, vhd.VirtualDiskAccessNone, vhd.OpenVirtualDiskFlagNone)
if err != nil {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("get scratch vhd info failed: %w", err)
}
defer func() {
if closeErr := syscall.CloseHandle(diskHandle); closeErr != nil {
log.G(ctx).WithFields(logrus.Fields{
"disk path": vhdxPath,
"error": closeErr,
}).Warn("failed to close vhd handle")
}
}()
err = vhd.AttachVirtualDisk(diskHandle, vhd.AttachVirtualDiskFlagNone, &vhd.AttachVirtualDiskParameters{Version: 2})
if err != nil {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("get scratch vhd info failed: %w", err)
}
defer func() {
if detachErr := vhd.DetachVirtualDisk(diskHandle); detachErr != nil {
log.G(ctx).WithFields(logrus.Fields{
"disk path": vhdxPath,
"error": detachErr,
}).Warn("failed to detach vhd")
}
}()
volumePath, err = vhd.GetVirtualDiskPhysicalPath(diskHandle)
if err != nil {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("get vhd physical path: %w", err)
}
driveLayout, partitions, err = getDriveLayout(ctx, volumePath)
if err != nil {
return ScratchVhdxPartitionInfo{}, err
}
if driveLayout.PartitionStyle != partitionStyleGPT {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("drive Layout:Expected partition style GPT(%d) found %d", partitionStyleGPT, driveLayout.PartitionStyle)
}
if driveLayout.PartitionCount != 2 || len(partitions) != 2 {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("expected exactly 2 partitions. Got %d partitions and partition count of %d", len(partitions), driveLayout.PartitionCount)
}
if partitions[1].PartitionStyle != partitionStyleGPT {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("partition Info:Expected partition style GPT(%d) found %d", partitionStyleGPT, partitions[1].PartitionStyle)
}
bufReader := bytes.NewBuffer(driveLayout.GptMbrUnion[:])
if err := binary.Read(bufReader, binary.LittleEndian, &gptDriveLayout); err != nil {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("failed to parse drive GPT layout: %w", err)
}
bufReader = bytes.NewBuffer(partitions[1].GptMbrUnion[:])
if err := binary.Read(bufReader, binary.LittleEndian, &gptPartitionInfo); err != nil {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("failed to parse GPT partition info: %w", err)
}
if gptPartitionInfo.PartitionType != partitionBasicDataGUID {
return ScratchVhdxPartitionInfo{}, fmt.Errorf("expected partition type to have %s GUID found %s instead", partitionBasicDataGUID, gptPartitionInfo.PartitionType)
}
log.G(ctx).WithFields(logrus.Fields{
"Disk ID": gptDriveLayout.DiskID,
"GPT Partition ID": gptPartitionInfo.PartitionId,
}).Debug("Scratch VHD partition info")
return ScratchVhdxPartitionInfo{DiskID: gptDriveLayout.DiskID, PartitionID: gptPartitionInfo.PartitionId}, nil
}

View File

@@ -104,7 +104,7 @@ func execute(ctx gcontext.Context, timeout time.Duration, f func() error) error
}()
select {
case <-ctx.Done():
if ctx.Err() == gcontext.DeadlineExceeded { //nolint:errorlint
if ctx.Err() == gcontext.DeadlineExceeded {
log.G(ctx).WithField(logfields.Timeout, trueTimeout).
Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. " +
"If it appears to be making no forward progress, obtain the stacks and see if there is a syscall " +

View File

@@ -1,289 +0,0 @@
//go:build windows
package cim
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/oc"
"github.com/Microsoft/hcsshim/internal/wclayer"
"github.com/Microsoft/hcsshim/osversion"
"github.com/Microsoft/hcsshim/pkg/cimfs"
"go.opencensus.io/trace"
)
// A CimLayerWriter implements the wclayer.LayerWriter interface to allow writing container
// image layers in the cim format.
// A cim layer consist of cim files (which are usually stored in the `cim-layers` directory and
// some other files which are stored in the directory of that layer (i.e the `path` directory).
type CimLayerWriter struct {
ctx context.Context
s *trace.Span
// path to the layer (i.e layer's directory) as provided by the caller.
// Even if a layer is stored as a cim in the cim directory, some files associated
// with a layer are still stored in this path.
path string
// parent layer paths
parentLayerPaths []string
// Handle to the layer cim - writes to the cim file
cimWriter *cimfs.CimFsWriter
// Handle to the writer for writing files in the local filesystem
stdFileWriter *stdFileWriter
// reference to currently active writer either cimWriter or stdFileWriter
activeWriter io.Writer
// denotes if this layer has the UtilityVM directory
hasUtilityVM bool
// some files are written outside the cim during initial import (via stdFileWriter) because we need to
// make some modifications to these files before writing them to the cim. The pendingOps slice
// maintains a list of such delayed modifications to the layer cim. These modifications are applied at
// the very end of layer import process.
pendingOps []pendingCimOp
}
type hive struct {
name string
base string
delta string
}
var (
hives = []hive{
{"SYSTEM", "SYSTEM_BASE", "SYSTEM_DELTA"},
{"SOFTWARE", "SOFTWARE_BASE", "SOFTWARE_DELTA"},
{"SAM", "SAM_BASE", "SAM_DELTA"},
{"SECURITY", "SECURITY_BASE", "SECURITY_DELTA"},
{"DEFAULT", "DEFAULTUSER_BASE", "DEFAULTUSER_DELTA"},
}
)
func isDeltaOrBaseHive(path string) bool {
for _, hv := range hives {
if strings.EqualFold(path, filepath.Join(wclayer.HivesPath, hv.delta)) ||
strings.EqualFold(path, filepath.Join(wclayer.RegFilesPath, hv.name)) {
return true
}
}
return false
}
// checks if this particular file should be written with a stdFileWriter instead of
// using the cimWriter.
func isStdFile(path string) bool {
return (isDeltaOrBaseHive(path) ||
path == filepath.Join(wclayer.UtilityVMPath, wclayer.RegFilesPath, "SYSTEM") ||
path == filepath.Join(wclayer.UtilityVMPath, wclayer.RegFilesPath, "SOFTWARE") ||
path == wclayer.BcdFilePath || path == wclayer.BootMgrFilePath)
}
// Add adds a file to the layer with given metadata.
func (cw *CimLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo, fileSize int64, securityDescriptor []byte, extendedAttributes []byte, reparseData []byte) error {
if name == wclayer.UtilityVMPath {
cw.hasUtilityVM = true
}
if isStdFile(name) {
// create a pending op for this file
cw.pendingOps = append(cw.pendingOps, &addOp{
pathInCim: name,
hostPath: filepath.Join(cw.path, name),
fileInfo: fileInfo,
securityDescriptor: securityDescriptor,
extendedAttributes: extendedAttributes,
reparseData: reparseData,
})
if err := cw.stdFileWriter.Add(name); err != nil {
return err
}
cw.activeWriter = cw.stdFileWriter
} else {
if err := cw.cimWriter.AddFile(name, fileInfo, fileSize, securityDescriptor, extendedAttributes, reparseData); err != nil {
return err
}
cw.activeWriter = cw.cimWriter
}
return nil
}
// AddLink adds a hard link to the layer. The target must already have been added.
func (cw *CimLayerWriter) AddLink(name string, target string) error {
// set active write to nil so that we panic if layer tar is incorrectly formatted.
cw.activeWriter = nil
if isStdFile(target) {
// If this is a link to a std file it will have to be added later once the
// std file is written to the CIM. Create a pending op for this
cw.pendingOps = append(cw.pendingOps, &linkOp{
oldPath: target,
newPath: name,
})
return nil
} else if isStdFile(name) {
// None of the predefined std files are links. If they show up as links this is unexpected
// behavior. Error out.
return fmt.Errorf("unexpected link %s in layer", name)
} else {
return cw.cimWriter.AddLink(target, name)
}
}
// AddAlternateStream creates another alternate stream at the given
// path. Any writes made after this call will go to that stream.
func (cw *CimLayerWriter) AddAlternateStream(name string, size uint64) error {
if isStdFile(name) {
// As of now there is no known case of std file having multiple data streams.
// If such a file is encountered our assumptions are wrong. Error out.
return fmt.Errorf("unexpected alternate stream %s in layer", name)
}
if err := cw.cimWriter.CreateAlternateStream(name, size); err != nil {
return err
}
cw.activeWriter = cw.cimWriter
return nil
}
// Remove removes a file that was present in a parent layer from the layer.
func (cw *CimLayerWriter) Remove(name string) error {
// set active write to nil so that we panic if layer tar is incorrectly formatted.
cw.activeWriter = nil
return cw.cimWriter.Unlink(name)
}
// Write writes data to the current file. The data must be in the format of a Win32
// backup stream.
func (cw *CimLayerWriter) Write(b []byte) (int, error) {
return cw.activeWriter.Write(b)
}
// Close finishes the layer writing process and releases any resources.
func (cw *CimLayerWriter) Close(ctx context.Context) (retErr error) {
if err := cw.stdFileWriter.Close(ctx); err != nil {
return err
}
// cimWriter must be closed even if there are errors.
defer func() {
if err := cw.cimWriter.Close(); retErr == nil {
retErr = err
}
}()
// Find out the osversion of this layer, both base & non-base layers can have UtilityVM layer.
processUtilityVM := false
if cw.hasUtilityVM {
uvmSoftwareHivePath := filepath.Join(cw.path, wclayer.UtilityVMPath, wclayer.RegFilesPath, "SOFTWARE")
osvStr, err := getOsBuildNumberFromRegistry(uvmSoftwareHivePath)
if err != nil {
return fmt.Errorf("read os version string from UtilityVM SOFTWARE hive: %w", err)
}
osv, err := strconv.ParseUint(osvStr, 10, 16)
if err != nil {
return fmt.Errorf("parse os version string (%s): %w", osvStr, err)
}
// write this version to a file for future reference by the shim process
if err = wclayer.WriteLayerUvmBuildFile(cw.path, uint16(osv)); err != nil {
return fmt.Errorf("write uvm build version: %w", err)
}
// CIMFS for hyperV isolated is only supported after 20348, processing UtilityVM layer on 2048
// & lower will cause failures since those images won't have CIMFS specific UVM files (mostly
// BCD entries required for CIMFS)
processUtilityVM = (osv > osversion.LTSC2022)
log.G(ctx).Debugf("import image os version %d, processing UtilityVM layer: %t\n", osv, processUtilityVM)
}
if len(cw.parentLayerPaths) == 0 {
if err := cw.processBaseLayer(ctx, processUtilityVM); err != nil {
return fmt.Errorf("process base layer: %w", err)
}
} else {
if err := cw.processNonBaseLayer(ctx, processUtilityVM); err != nil {
return fmt.Errorf("process non base layer: %w", err)
}
}
for _, op := range cw.pendingOps {
if err := op.apply(cw.cimWriter); err != nil {
return fmt.Errorf("apply pending operations: %w", err)
}
}
return nil
}
func NewCimLayerWriter(ctx context.Context, path string, parentLayerPaths []string) (_ *CimLayerWriter, err error) {
if !cimfs.IsCimFSSupported() {
return nil, fmt.Errorf("CimFs not supported on this build")
}
ctx, span := trace.StartSpan(ctx, "hcsshim::NewCimLayerWriter")
defer func() {
if err != nil {
oc.SetSpanStatus(span, err)
span.End()
}
}()
span.AddAttributes(
trace.StringAttribute("path", path),
trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", ")))
parentCim := ""
cimDirPath := GetCimDirFromLayer(path)
if _, err = os.Stat(cimDirPath); os.IsNotExist(err) {
// create cim directory
if err = os.Mkdir(cimDirPath, 0755); err != nil {
return nil, fmt.Errorf("failed while creating cim layers directory: %w", err)
}
} else if err != nil {
return nil, fmt.Errorf("unable to access cim layers directory: %w", err)
}
if len(parentLayerPaths) > 0 {
parentCim = GetCimNameFromLayer(parentLayerPaths[0])
}
cim, err := cimfs.Create(cimDirPath, parentCim, GetCimNameFromLayer(path))
if err != nil {
return nil, fmt.Errorf("error in creating a new cim: %w", err)
}
sfw, err := newStdFileWriter(path, parentLayerPaths)
if err != nil {
return nil, fmt.Errorf("error in creating new standard file writer: %w", err)
}
return &CimLayerWriter{
ctx: ctx,
s: span,
path: path,
parentLayerPaths: parentLayerPaths,
cimWriter: cim,
stdFileWriter: sfw,
}, nil
}
// DestroyCimLayer destroys a cim layer i.e it removes all the cimfs files for the given layer as well as
// all of the other files that are stored in the layer directory (at path `layerPath`).
// If this is not a cimfs layer (i.e a cim file for the given layer does not exist) then nothing is done.
func DestroyCimLayer(ctx context.Context, layerPath string) error {
cimPath := GetCimPathFromLayer(layerPath)
// verify that such a cim exists first, sometimes containerd tries to call
// this with the root snapshot directory as the layer path. We don't want to
// destroy everything inside the snapshots directory.
if _, err := os.Stat(cimPath); err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
return cimfs.DestroyCim(ctx, cimPath)
}

View File

@@ -1,107 +0,0 @@
//go:build windows
package cim
import (
"bytes"
"fmt"
"os/exec"
"github.com/Microsoft/go-winio/pkg/guid"
)
const (
bcdFilePath = "UtilityVM\\Files\\EFI\\Microsoft\\Boot\\BCD"
cimfsDeviceOptionsID = "{763e9fea-502d-434f-aad9-5fabe9c91a7b}"
vmbusDeviceID = "{c63c9bdf-5fa5-4208-b03f-6b458b365592}"
compositeDeviceOptionsID = "{e1787220-d17f-49e7-977a-d8fe4c8537e2}"
bootContainerID = "{b890454c-80de-4e98-a7ab-56b74b4fbd0c}"
)
func bcdExec(storePath string, args ...string) error {
var out bytes.Buffer
argsArr := []string{"/store", storePath, "/offline"}
argsArr = append(argsArr, args...)
cmd := exec.Command("bcdedit.exe", argsArr...)
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
return fmt.Errorf("bcd command (%s) failed: %w", cmd, err)
}
return nil
}
// A registry configuration required for the uvm.
func setBcdRestartOnFailure(storePath string) error {
return bcdExec(storePath, "/set", "{default}", "restartonfailure", "yes")
}
func setBcdCimBootDevice(storePath, cimPathRelativeToVSMB string, diskID, partitionID guid.GUID) error {
// create options for cimfs boot device
if err := bcdExec(storePath, "/create", cimfsDeviceOptionsID, "/d", "CimFS Device Options", "/device"); err != nil {
return err
}
// Set options. For now we need to set 2 options. First is the parent device i.e the device under
// which all cim files will be available. Second is the path of the cim (from which this UVM should
// boot) relative to the parent device. Note that even though the 2nd option is named
// `cimfsrootdirectory` it expects a path to the cim file and not a directory path.
if err := bcdExec(storePath, "/set", cimfsDeviceOptionsID, "cimfsparentdevice", fmt.Sprintf("vmbus=%s", vmbusDeviceID)); err != nil {
return err
}
if err := bcdExec(storePath, "/set", cimfsDeviceOptionsID, "cimfsrootdirectory", fmt.Sprintf("\\%s", cimPathRelativeToVSMB)); err != nil {
return err
}
// create options for the composite device
if err := bcdExec(storePath, "/create", compositeDeviceOptionsID, "/d", "Composite Device Options", "/device"); err != nil {
return err
}
// We need to specify the diskID & the partition ID of the boot disk and we need to set the cimfs boot
// options ID
partitionStr := fmt.Sprintf("gpt_partition={%s};{%s}", diskID, partitionID)
if err := bcdExec(storePath, "/set", compositeDeviceOptionsID, "primarydevice", partitionStr); err != nil {
return err
}
if err := bcdExec(storePath, "/set", compositeDeviceOptionsID, "secondarydevice", fmt.Sprintf("cimfs=%s,%s", bootContainerID, cimfsDeviceOptionsID)); err != nil {
return err
}
if err := bcdExec(storePath, "/set", "{default}", "device", fmt.Sprintf("composite=0,%s", compositeDeviceOptionsID)); err != nil {
return err
}
if err := bcdExec(storePath, "/set", "{default}", "osdevice", fmt.Sprintf("composite=0,%s", compositeDeviceOptionsID)); err != nil {
return err
}
// Since our UVM file are stored under UtilityVM\Files directory inside the CIM we must prepend that
// directory in front of paths used by bootmgr
if err := bcdExec(storePath, "/set", "{default}", "path", "\\UtilityVM\\Files\\Windows\\System32\\winload.efi"); err != nil {
return err
}
if err := bcdExec(storePath, "/set", "{default}", "systemroot", "\\UtilityVM\\Files\\Windows"); err != nil {
return err
}
return nil
}
// updateBcdStoreForBoot Updates the bcd store at path layerPath + UtilityVM\Files\EFI\Microsoft\Boot\BCD` to
// boot with the disk with given ID and given partitionID. cimPathRelativeToVSMB is the path of the cim which
// will be used for booting this UVM relative to the VSMB share. (Usually, the entire snapshots directory will
// be shared over VSMB, so if this is the cim-layers\1.cim under that directory, the value of
// `cimPathRelativeToVSMB` should be cim-layers\1.cim)
func updateBcdStoreForBoot(storePath string, cimPathRelativeToVSMB string, diskID, partitionID guid.GUID) error {
if err := setBcdRestartOnFailure(storePath); err != nil {
return err
}
if err := setBcdCimBootDevice(storePath, cimPathRelativeToVSMB, diskID, partitionID); err != nil {
return err
}
return nil
}

View File

@@ -1,41 +0,0 @@
//go:build windows
package cim
import (
"os"
"path/filepath"
)
const (
// name of the directory in which cims are stored
cimDir = "cim-layers"
)
// Usually layers are stored at ./root/io.containerd.snapshotter.v1.windows/snapshots/<layerid>. For cimfs we
// must store all layer cims in the same directory (for forked cims to work). So all cim layers are stored in
// /root/io.containerd.snapshotter.v1.windows/snapshots/cim-layers. And the cim file representing each
// individual layer is stored at /root/io.containerd.snapshotter.v1.windows/snapshots/cim-layers/<layerid>.cim
// CimName is the filename (<layerid>.cim) of the file representing the cim
func GetCimNameFromLayer(layerPath string) string {
return filepath.Base(layerPath) + ".cim"
}
// CimPath is the path to the CimDir/<layerid>.cim file that represents a layer cim.
func GetCimPathFromLayer(layerPath string) string {
return filepath.Join(GetCimDirFromLayer(layerPath), GetCimNameFromLayer(layerPath))
}
// CimDir is the directory inside which all cims are stored.
func GetCimDirFromLayer(layerPath string) string {
dir := filepath.Dir(layerPath)
return filepath.Join(dir, cimDir)
}
// IsCimLayer returns `true` if the layer at path `layerPath` is a cim layer. Returns `false` otherwise.
func IsCimLayer(layerPath string) bool {
cimPath := GetCimPathFromLayer(layerPath)
_, err := os.Stat(cimPath)
return (err == nil)
}

View File

@@ -1,3 +0,0 @@
// This package provides utilities for working with container image layers in the cim format
// via the wclayer APIs.
package cim

View File

@@ -1,90 +0,0 @@
//go:build windows
package cim
import (
"context"
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/internal/safefile"
"github.com/Microsoft/hcsshim/internal/winapi"
)
// stdFileWriter writes the files of a layer to the layer folder instead of writing them inside the cim.
// For some files (like the Hive files or some UtilityVM files) it is necessary to write them as a normal file
// first, do some modifications on them (for example merging of hives or processing of UtilityVM files)
// and then write the modified versions into the cim. This writer is used for such files.
type stdFileWriter struct {
activeFile *os.File
// parent layer paths
parentLayerPaths []string
// path to the current layer
path string
// the open handle to the path directory
root *os.File
}
func newStdFileWriter(root string, parentRoots []string) (sfw *stdFileWriter, err error) {
sfw = &stdFileWriter{
path: root,
parentLayerPaths: parentRoots,
}
sfw.root, err = safefile.OpenRoot(root)
if err != nil {
return
}
return
}
func (sfw *stdFileWriter) closeActiveFile() (err error) {
if sfw.activeFile != nil {
err = sfw.activeFile.Close()
sfw.activeFile = nil
}
return
}
// Adds a new file or an alternate data stream to an existing file inside the layer directory.
func (sfw *stdFileWriter) Add(name string) error {
if err := sfw.closeActiveFile(); err != nil {
return err
}
// The directory of this file might be created inside the cim.
// make sure we have the same parent directory chain here
if err := safefile.MkdirAllRelative(filepath.Dir(name), sfw.root); err != nil {
return fmt.Errorf("failed to create file %s: %w", name, err)
}
f, err := safefile.OpenRelative(
name,
sfw.root,
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER,
syscall.FILE_SHARE_READ,
winapi.FILE_CREATE,
0,
)
if err != nil {
return fmt.Errorf("error creating file %s: %w", name, err)
}
sfw.activeFile = f
return nil
}
// Write writes data to the current file. The data must be in the format of a Win32
// backup stream.
func (sfw *stdFileWriter) Write(b []byte) (int, error) {
return sfw.activeFile.Write(b)
}
// Close finishes the layer writing process and releases any resources.
func (sfw *stdFileWriter) Close(ctx context.Context) error {
if err := sfw.closeActiveFile(); err != nil {
return fmt.Errorf("failed to close active file %s : %w", sfw.activeFile.Name(), err)
}
return nil
}

View File

@@ -1,89 +0,0 @@
//go:build windows
package cim
import (
"context"
"fmt"
"os"
"sync"
"github.com/Microsoft/go-winio/pkg/guid"
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
cimfs "github.com/Microsoft/hcsshim/pkg/cimfs"
)
// a cache of cim layer to its mounted volume - The mount manager plugin currently doesn't have an option of
// querying a mounted cim to get the volume at which it is mounted, so we maintain a cache of that here
var (
cimMounts map[string]string = make(map[string]string)
cimMountMapLock sync.Mutex
// A random GUID used as a namespace for generating cim mount volume GUIDs: 6827367b-c388-4e9b-95ec-961c6d2c936c
cimMountNamespace guid.GUID = guid.GUID{Data1: 0x6827367b, Data2: 0xc388, Data3: 0x4e9b, Data4: [8]byte{0x96, 0x1c, 0x6d, 0x2c, 0x93, 0x6c}}
)
// MountCimLayer mounts the cim at path `cimPath` and returns the mount location of that cim. This method
// uses the `CimMountFlagCacheFiles` mount flag when mounting the cim. The containerID is used to generated
// the volumeID for the volume at which this CIM is mounted. containerID is used so that if the shim process
// crashes for any reason, the mounted cim can be correctly cleaned up during `shim delete` call.
func MountCimLayer(ctx context.Context, cimPath, containerID string) (string, error) {
volumeGUID, err := guid.NewV5(cimMountNamespace, []byte(containerID))
if err != nil {
return "", fmt.Errorf("generated cim mount GUID: %w", err)
}
vol, err := cimfs.Mount(cimPath, volumeGUID, hcsschema.CimMountFlagCacheFiles)
if err != nil {
return "", err
}
cimMountMapLock.Lock()
defer cimMountMapLock.Unlock()
cimMounts[fmt.Sprintf("%s_%s", containerID, cimPath)] = vol
return vol, nil
}
// Unmount unmounts the cim at mounted for given container.
func UnmountCimLayer(ctx context.Context, cimPath, containerID string) error {
cimMountMapLock.Lock()
defer cimMountMapLock.Unlock()
if vol, ok := cimMounts[fmt.Sprintf("%s_%s", containerID, cimPath)]; !ok {
return fmt.Errorf("cim %s not mounted", cimPath)
} else {
delete(cimMounts, fmt.Sprintf("%s_%s", containerID, cimPath))
err := cimfs.Unmount(vol)
if err != nil {
return err
}
}
return nil
}
// GetCimMountPath returns the volume at which a cim is mounted. If the cim is not mounted returns error
func GetCimMountPath(cimPath, containerID string) (string, error) {
cimMountMapLock.Lock()
defer cimMountMapLock.Unlock()
if vol, ok := cimMounts[fmt.Sprintf("%s_%s", containerID, cimPath)]; !ok {
return "", fmt.Errorf("cim %s not mounted", cimPath)
} else {
return vol, nil
}
}
func CleanupContainerMounts(containerID string) error {
volumeGUID, err := guid.NewV5(cimMountNamespace, []byte(containerID))
if err != nil {
return fmt.Errorf("generated cim mount GUID: %w", err)
}
volPath := fmt.Sprintf("\\\\?\\Volume{%s}\\", volumeGUID.String())
if _, err := os.Stat(volPath); err == nil {
err = cimfs.Unmount(volPath)
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,68 +0,0 @@
//go:build windows
package cim
import (
"fmt"
"io"
"os"
"github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/pkg/cimfs"
"golang.org/x/sys/windows"
)
type pendingCimOp interface {
apply(cw *cimfs.CimFsWriter) error
}
// add op represents a pending operation of adding a new file inside the cim
type addOp struct {
// path inside the cim at which the file should be added
pathInCim string
// host path where this file was temporarily written.
hostPath string
// other file metadata fields that were provided during the add call.
fileInfo *winio.FileBasicInfo
securityDescriptor []byte
extendedAttributes []byte
reparseData []byte
}
func (o *addOp) apply(cw *cimfs.CimFsWriter) error {
f, err := os.Open(o.hostPath)
if err != nil {
return fmt.Errorf("open file %s: %w", o.hostPath, err)
}
defer f.Close()
fs, err := f.Stat()
if err != nil {
return fmt.Errorf("stat file %s: %w", o.hostPath, err)
}
if err := cw.AddFile(o.pathInCim, o.fileInfo, fs.Size(), o.securityDescriptor, o.extendedAttributes, o.reparseData); err != nil {
return fmt.Errorf("cim add file %s: %w", o.hostPath, err)
}
if o.fileInfo.FileAttributes != windows.FILE_ATTRIBUTE_DIRECTORY {
written, err := io.Copy(cw, f)
if err != nil {
return fmt.Errorf("write file %s inside cim: %w", o.hostPath, err)
} else if written != fs.Size() {
return fmt.Errorf("short write to cim for file %s, expected %d bytes wrote %d", o.hostPath, fs.Size(), written)
}
}
return nil
}
// linkOp represents a pending link file operation inside the cim
type linkOp struct {
// old & new paths inside the cim where the link should be created
oldPath string
newPath string
}
func (o *linkOp) apply(cw *cimfs.CimFsWriter) error {
return cw.AddLink(o.oldPath, o.newPath)
}

View File

@@ -1,230 +0,0 @@
//go:build windows
package cim
import (
"context"
"fmt"
"os"
"path/filepath"
"syscall"
"time"
"github.com/Microsoft/go-winio"
"github.com/Microsoft/go-winio/vhd"
"github.com/Microsoft/hcsshim/computestorage"
"github.com/Microsoft/hcsshim/internal/memory"
"github.com/Microsoft/hcsshim/internal/security"
"github.com/Microsoft/hcsshim/internal/vhdx"
"github.com/Microsoft/hcsshim/internal/wclayer"
"golang.org/x/sys/windows"
)
const defaultVHDXBlockSizeInMB = 1
// processUtilityVMLayer is similar to createContainerBaseLayerVHDs but along with the scratch creation it
// also does some BCD modifications to allow the UVM to boot from the CIM. It expects that the UVM BCD file is
// present at layerPath/`wclayer.BcdFilePath` and a UVM SYSTEM hive is present at
// layerPath/UtilityVM/`wclayer.RegFilesPath`/SYSTEM. The scratch VHDs are created under the `layerPath`
// directory.
func processUtilityVMLayer(ctx context.Context, layerPath string) error {
// func createUtilityVMLayerVHDs(ctx context.Context, layerPath string) error {
baseVhdPath := filepath.Join(layerPath, wclayer.UtilityVMPath, wclayer.UtilityVMBaseVhd)
diffVhdPath := filepath.Join(layerPath, wclayer.UtilityVMPath, wclayer.UtilityVMScratchVhd)
defaultVhdSize := uint64(10)
// Just create the vhdx for utilityVM layer, no need to format it.
createParams := &vhd.CreateVirtualDiskParameters{
Version: 2,
Version2: vhd.CreateVersion2{
MaximumSize: defaultVhdSize * memory.GiB,
BlockSizeInBytes: defaultVHDXBlockSizeInMB * memory.MiB,
},
}
handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams)
if err != nil {
return fmt.Errorf("failed to create vhdx: %w", err)
}
defer func() {
if err != nil {
os.RemoveAll(baseVhdPath)
os.RemoveAll(diffVhdPath)
}
}()
err = computestorage.FormatWritableLayerVhd(ctx, windows.Handle(handle))
closeErr := syscall.CloseHandle(handle)
if err != nil {
return err
} else if closeErr != nil {
return fmt.Errorf("failed to close vhdx handle: %w", closeErr)
}
partitionInfo, err := vhdx.GetScratchVhdPartitionInfo(ctx, baseVhdPath)
if err != nil {
return fmt.Errorf("failed to get base vhd layout info: %w", err)
}
// relativeCimPath needs to be the cim path relative to the snapshots directory. The snapshots
// directory is shared inside the UVM over VSMB, so during the UVM boot this relative path will be
// used to find the cim file under that VSMB share.
relativeCimPath := filepath.Join(filepath.Base(GetCimDirFromLayer(layerPath)), GetCimNameFromLayer(layerPath))
bcdPath := filepath.Join(layerPath, bcdFilePath)
if err = updateBcdStoreForBoot(bcdPath, relativeCimPath, partitionInfo.DiskID, partitionInfo.PartitionID); err != nil {
return fmt.Errorf("failed to update BCD: %w", err)
}
if err := enableCimBoot(filepath.Join(layerPath, wclayer.UtilityVMPath, wclayer.RegFilesPath, "SYSTEM")); err != nil {
return fmt.Errorf("failed to setup cim image for uvm boot: %w", err)
}
// Note: diff vhd creation and granting of vm group access must be done AFTER
// getting the partition info of the base VHD. Otherwise it causes the vhd parent
// chain to get corrupted.
// TODO(ambarve): figure out why this happens so that bcd update can be moved to a separate function
// Create the differencing disk that will be what's copied for the final rw layer
// for a container.
if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil {
return fmt.Errorf("failed to create differencing disk: %w", err)
}
if err := security.GrantVmGroupAccess(baseVhdPath); err != nil {
return fmt.Errorf("failed to grant vm group access to %s: %w", baseVhdPath, err)
}
if err := security.GrantVmGroupAccess(diffVhdPath); err != nil {
return fmt.Errorf("failed to grant vm group access to %s: %w", diffVhdPath, err)
}
return nil
}
// processBaseLayerHives make the base layer specific modifications on the hives and emits equivalent the
// pendingCimOps that should be applied on the CIM. In base layer we need to create hard links from registry
// hives under Files/Windows/Sysetm32/config into Hives/*_BASE. This function creates these links outside so
// that the registry hives under Hives/ are available during children layers import. Then we write these hive
// files inside the cim and create links inside the cim.
func processBaseLayerHives(layerPath string) ([]pendingCimOp, error) {
pendingOps := []pendingCimOp{}
// make hives directory both outside and in the cim
if err := os.Mkdir(filepath.Join(layerPath, wclayer.HivesPath), 0755); err != nil {
return pendingOps, fmt.Errorf("hives directory creation: %w", err)
}
hivesDirInfo := &winio.FileBasicInfo{
CreationTime: windows.NsecToFiletime(time.Now().UnixNano()),
LastAccessTime: windows.NsecToFiletime(time.Now().UnixNano()),
LastWriteTime: windows.NsecToFiletime(time.Now().UnixNano()),
ChangeTime: windows.NsecToFiletime(time.Now().UnixNano()),
FileAttributes: windows.FILE_ATTRIBUTE_DIRECTORY,
}
pendingOps = append(pendingOps, &addOp{
pathInCim: wclayer.HivesPath,
hostPath: filepath.Join(layerPath, wclayer.HivesPath),
fileInfo: hivesDirInfo,
})
// add hard links from base hive files.
for _, hv := range hives {
oldHivePathRelative := filepath.Join(wclayer.RegFilesPath, hv.name)
newHivePathRelative := filepath.Join(wclayer.HivesPath, hv.base)
if err := os.Link(filepath.Join(layerPath, oldHivePathRelative), filepath.Join(layerPath, newHivePathRelative)); err != nil {
return pendingOps, fmt.Errorf("hive link creation: %w", err)
}
pendingOps = append(pendingOps, &linkOp{
oldPath: oldHivePathRelative,
newPath: newHivePathRelative,
})
}
return pendingOps, nil
}
// processLayoutFile creates a file named "layout" in the root of the base layer. This allows certain
// container startup related functions to understand that the hives are a part of the container rootfs.
func processLayoutFile(layerPath string) ([]pendingCimOp, error) {
fileContents := "vhd-with-hives\n"
if err := os.WriteFile(filepath.Join(layerPath, "layout"), []byte(fileContents), 0755); err != nil {
return []pendingCimOp{}, fmt.Errorf("write layout file: %w", err)
}
layoutFileInfo := &winio.FileBasicInfo{
CreationTime: windows.NsecToFiletime(time.Now().UnixNano()),
LastAccessTime: windows.NsecToFiletime(time.Now().UnixNano()),
LastWriteTime: windows.NsecToFiletime(time.Now().UnixNano()),
ChangeTime: windows.NsecToFiletime(time.Now().UnixNano()),
FileAttributes: windows.FILE_ATTRIBUTE_NORMAL,
}
op := &addOp{
pathInCim: "layout",
hostPath: filepath.Join(layerPath, "layout"),
fileInfo: layoutFileInfo,
}
return []pendingCimOp{op}, nil
}
// Some of the layer files that are generated during the processBaseLayer call must be added back
// inside the cim, some registry file links must be updated. This function takes care of all those
// steps. This function opens the cim file for writing and updates it.
func (cw *CimLayerWriter) processBaseLayer(ctx context.Context, processUtilityVM bool) (err error) {
if processUtilityVM {
if err = processUtilityVMLayer(ctx, cw.path); err != nil {
return fmt.Errorf("process utilityVM layer: %w", err)
}
}
ops, err := processBaseLayerHives(cw.path)
if err != nil {
return err
}
cw.pendingOps = append(cw.pendingOps, ops...)
ops, err = processLayoutFile(cw.path)
if err != nil {
return err
}
cw.pendingOps = append(cw.pendingOps, ops...)
return nil
}
// processNonBaseLayer takes care of the processing required for a non base layer. As of now
// the only processing required for non base layer is to merge the delta registry hives of the
// non-base layer with it's parent layer.
func (cw *CimLayerWriter) processNonBaseLayer(ctx context.Context, processUtilityVM bool) (err error) {
for _, hv := range hives {
baseHive := filepath.Join(wclayer.HivesPath, hv.base)
deltaHive := filepath.Join(wclayer.HivesPath, hv.delta)
_, err := os.Stat(filepath.Join(cw.path, deltaHive))
// merge with parent layer if delta exists.
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("stat delta hive %s: %w", filepath.Join(cw.path, deltaHive), err)
} else if err == nil {
// merge base hive of parent layer with the delta hive of this layer and write it as
// the base hive of this layer.
err = mergeHive(filepath.Join(cw.parentLayerPaths[0], baseHive), filepath.Join(cw.path, deltaHive), filepath.Join(cw.path, baseHive))
if err != nil {
return err
}
// the newly created merged file must be added to the cim
cw.pendingOps = append(cw.pendingOps, &addOp{
pathInCim: baseHive,
hostPath: filepath.Join(cw.path, baseHive),
fileInfo: &winio.FileBasicInfo{
CreationTime: windows.NsecToFiletime(time.Now().UnixNano()),
LastAccessTime: windows.NsecToFiletime(time.Now().UnixNano()),
LastWriteTime: windows.NsecToFiletime(time.Now().UnixNano()),
ChangeTime: windows.NsecToFiletime(time.Now().UnixNano()),
FileAttributes: windows.FILE_ATTRIBUTE_NORMAL,
},
})
}
}
if processUtilityVM {
return processUtilityVMLayer(ctx, cw.path)
}
return nil
}

View File

@@ -1,172 +0,0 @@
//go:build windows
package cim
import (
"encoding/binary"
"fmt"
"os"
"unsafe"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/winapi"
"github.com/Microsoft/hcsshim/osversion"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
)
// enableCimBoot Opens the SYSTEM registry hive at path `hivePath` and updates it to include a CIMFS Start
// registry key. This prepares the uvm to boot from a cim file if requested. The registry changes required to
// actually make the uvm boot from a cim will be added in the uvm config (look at
// addBootFromCimRegistryChanges for details). This registry key needs to be available in the early boot
// phase and so including it in the uvm config doesn't work.
func enableCimBoot(hivePath string) (err error) {
dataZero := make([]byte, 4)
dataOne := make([]byte, 4)
binary.LittleEndian.PutUint32(dataOne, 1)
dataFour := make([]byte, 4)
binary.LittleEndian.PutUint32(dataFour, 4)
bootGUID, err := windows.UTF16FromString(bootContainerID)
if err != nil {
return fmt.Errorf("failed to encode boot guid to utf16: %w", err)
}
overrideBootPath, err := windows.UTF16FromString("\\Windows\\")
if err != nil {
return fmt.Errorf("failed to encode override boot path to utf16: %w", err)
}
regChanges := []struct {
keyPath string
valueName string
valueType winapi.RegType
data *byte
dataLen uint32
}{
{"ControlSet001\\Control", "BootContainerGuid", winapi.REG_TYPE_SZ, (*byte)(unsafe.Pointer(&bootGUID[0])), 2 * uint32(len(bootGUID))},
{"ControlSet001\\Services\\UnionFS", "Start", winapi.REG_TYPE_DWORD, &dataZero[0], uint32(len(dataZero))},
{"ControlSet001\\Services\\wcifs", "Start", winapi.REG_TYPE_DWORD, &dataFour[0], uint32(len(dataZero))},
// The bootmgr loads the uvm files from the cim and so uses the relative path `UtilityVM\\Files` inside the cim to access the uvm files. However, once the cim is mounted UnionFS will merge the correct directory (UtilityVM\\Files) of the cim with the scratch and then that point onwards we don't need to use the relative path. Below registry key tells the kernel that the boot path that was provided in BCD should now be overriden with this new path.
{"Setup", "BootPathOverride", winapi.REG_TYPE_SZ, (*byte)(unsafe.Pointer(&overrideBootPath[0])), 2 * uint32(len(overrideBootPath))},
}
var storeHandle winapi.ORHKey
if err = winapi.OROpenHive(hivePath, &storeHandle); err != nil {
return fmt.Errorf("failed to open registry store at %s: %w", hivePath, err)
}
for _, change := range regChanges {
var changeKey winapi.ORHKey
if err = winapi.ORCreateKey(storeHandle, change.keyPath, 0, 0, 0, &changeKey, nil); err != nil {
return fmt.Errorf("failed to open reg key %s: %w", change.keyPath, err)
}
if err = winapi.ORSetValue(changeKey, change.valueName, uint32(change.valueType), change.data, change.dataLen); err != nil {
return fmt.Errorf("failed to set value for regkey %s\\%s : %w", change.keyPath, change.valueName, err)
}
}
// remove the existing file first
if err := os.Remove(hivePath); err != nil {
return fmt.Errorf("failed to remove existing registry %s: %w", hivePath, err)
}
if err = winapi.ORSaveHive(winapi.ORHKey(storeHandle), hivePath, uint32(osversion.Get().MajorVersion), uint32(osversion.Get().MinorVersion)); err != nil {
return fmt.Errorf("error saving the registry store: %w", err)
}
// close hive irrespective of the errors
if err := winapi.ORCloseHive(winapi.ORHKey(storeHandle)); err != nil {
return fmt.Errorf("error closing registry store; %w", err)
}
return nil
}
// mergeHive merges the hive located at parentHivePath with the hive located at deltaHivePath and stores
// the result into the file at mergedHivePath. If a file already exists at path `mergedHivePath` then it
// throws an error.
func mergeHive(parentHivePath, deltaHivePath, mergedHivePath string) (err error) {
var baseHive, deltaHive, mergedHive winapi.ORHKey
if err := winapi.OROpenHive(parentHivePath, &baseHive); err != nil {
return fmt.Errorf("failed to open base hive %s: %w", parentHivePath, err)
}
defer func() {
err2 := winapi.ORCloseHive(baseHive)
if err == nil {
err = errors.Wrap(err2, "failed to close base hive")
}
}()
if err := winapi.OROpenHive(deltaHivePath, &deltaHive); err != nil {
return fmt.Errorf("failed to open delta hive %s: %w", deltaHivePath, err)
}
defer func() {
err2 := winapi.ORCloseHive(deltaHive)
if err == nil {
err = errors.Wrap(err2, "failed to close delta hive")
}
}()
if err := winapi.ORMergeHives([]winapi.ORHKey{baseHive, deltaHive}, &mergedHive); err != nil {
return fmt.Errorf("failed to merge hives: %w", err)
}
defer func() {
err2 := winapi.ORCloseHive(mergedHive)
if err == nil {
err = errors.Wrap(err2, "failed to close merged hive")
}
}()
if err := winapi.ORSaveHive(mergedHive, mergedHivePath, uint32(osversion.Get().MajorVersion), uint32(osversion.Get().MinorVersion)); err != nil {
return fmt.Errorf("failed to save hive: %w", err)
}
return
}
// getOsBuildNumberFromRegistry fetches the "CurrentBuild" value at path
// "Microsoft\Windows NT\CurrentVersion" from the SOFTWARE registry hive at path
// `regHivePath`. This is used to detect the build version of the uvm.
func getOsBuildNumberFromRegistry(regHivePath string) (_ string, err error) {
var storeHandle, keyHandle winapi.ORHKey
var dataType, dataLen uint32
keyPath := "Microsoft\\Windows NT\\CurrentVersion"
valueName := "CurrentBuild"
dataLen = 16 // build version string can't be more than 5 wide chars?
dataBuf := make([]byte, dataLen)
if err = winapi.OROpenHive(regHivePath, &storeHandle); err != nil {
return "", fmt.Errorf("failed to open registry store at %s: %w", regHivePath, err)
}
defer func() {
if closeErr := winapi.ORCloseHive(storeHandle); closeErr != nil {
log.L.WithFields(logrus.Fields{
"error": closeErr,
"hive": regHivePath,
}).Warnf("failed to close hive")
}
}()
if err = winapi.OROpenKey(storeHandle, keyPath, &keyHandle); err != nil {
return "", fmt.Errorf("failed to open key at %s: %w", keyPath, err)
}
defer func() {
if closeErr := winapi.ORCloseKey(keyHandle); closeErr != nil {
log.L.WithFields(logrus.Fields{
"error": closeErr,
"hive": regHivePath,
"key": keyPath,
"value": valueName,
}).Warnf("failed to close hive key")
}
}()
if err = winapi.ORGetValue(keyHandle, "", valueName, &dataType, &dataBuf[0], &dataLen); err != nil {
return "", fmt.Errorf("failed to get value of %s: %w", valueName, err)
}
if dataType != uint32(winapi.REG_TYPE_SZ) {
return "", fmt.Errorf("unexpected build number data type (%d)", dataType)
}
return winapi.ParseUtf16LE(dataBuf[:(dataLen - 2)]), nil
}

View File

@@ -34,6 +34,7 @@ const (
UtilityVMPath = `UtilityVM`
UtilityVMFilesPath = `UtilityVM\Files`
RegFilesPath = `Files\Windows\System32\config`
BootDirRelativePath = `\EFI\Microsoft\Boot`
BcdFilePath = `UtilityVM\Files\EFI\Microsoft\Boot\BCD`
BootMgrFilePath = `UtilityVM\Files\EFI\Microsoft\Boot\bootmgfw.efi`
ContainerBaseVhd = `blank-base.vhdx`

View File

@@ -32,10 +32,16 @@ type CimFsFileMetadata struct {
EACount uint32
}
type CimFsImagePath struct {
ImageDir *uint16
ImageName *uint16
}
//sys CimMountImage(imagePath string, fsName string, flags uint32, volumeID *g) (hr error) = cimfs.CimMountImage?
//sys CimDismountImage(volumeID *g) (hr error) = cimfs.CimDismountImage?
//sys CimCreateImage(imagePath string, oldFSName *uint16, newFSName *uint16, cimFSHandle *FsHandle) (hr error) = cimfs.CimCreateImage?
//sys CimCreateImage2(imagePath string, flags uint32, oldFSName *uint16, newFSName *uint16, cimFSHandle *FsHandle) (hr error) = cimfs.CimCreateImage2?
//sys CimCloseImage(cimFSHandle FsHandle) = cimfs.CimCloseImage?
//sys CimCommitImage(cimFSHandle FsHandle) (hr error) = cimfs.CimCommitImage?
@@ -45,3 +51,8 @@ type CimFsFileMetadata struct {
//sys CimDeletePath(cimFSHandle FsHandle, path string) (hr error) = cimfs.CimDeletePath?
//sys CimCreateHardLink(cimFSHandle FsHandle, newPath string, oldPath string) (hr error) = cimfs.CimCreateHardLink?
//sys CimCreateAlternateStream(cimFSHandle FsHandle, path string, size uint64, cimStreamHandle *StreamHandle) (hr error) = cimfs.CimCreateAlternateStream?
//sys CimAddFsToMergedImage(cimFSHandle FsHandle, path string) (hr error) = cimfs.CimAddFsToMergedImage?
//sys CimAddFsToMergedImage2(cimFSHandle FsHandle, path string, flags uint32) (hr error) = cimfs.CimAddFsToMergedImage2?
//sys CimMergeMountImage(numCimPaths uint32, backingImagePaths *CimFsImagePath, flags uint32, volumeID *g) (hr error) = cimfs.CimMergeMountImage?
//sys CimTombstoneFile(cimFSHandle FsHandle, path string) (hr error) = cimfs.CimTombstoneFile?
//sys CimCreateMergeLink(cimFSHandle FsHandle, newPath string, oldPath string) (hr error) = cimfs.CimCreateMergeLink?

View File

@@ -53,6 +53,8 @@ var (
procCM_Get_Device_ID_ListA = modcfgmgr32.NewProc("CM_Get_Device_ID_ListA")
procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA")
procCM_Locate_DevNodeW = modcfgmgr32.NewProc("CM_Locate_DevNodeW")
procCimAddFsToMergedImage = modcimfs.NewProc("CimAddFsToMergedImage")
procCimAddFsToMergedImage2 = modcimfs.NewProc("CimAddFsToMergedImage2")
procCimCloseImage = modcimfs.NewProc("CimCloseImage")
procCimCloseStream = modcimfs.NewProc("CimCloseStream")
procCimCommitImage = modcimfs.NewProc("CimCommitImage")
@@ -60,9 +62,13 @@ var (
procCimCreateFile = modcimfs.NewProc("CimCreateFile")
procCimCreateHardLink = modcimfs.NewProc("CimCreateHardLink")
procCimCreateImage = modcimfs.NewProc("CimCreateImage")
procCimCreateImage2 = modcimfs.NewProc("CimCreateImage2")
procCimCreateMergeLink = modcimfs.NewProc("CimCreateMergeLink")
procCimDeletePath = modcimfs.NewProc("CimDeletePath")
procCimDismountImage = modcimfs.NewProc("CimDismountImage")
procCimMergeMountImage = modcimfs.NewProc("CimMergeMountImage")
procCimMountImage = modcimfs.NewProc("CimMountImage")
procCimTombstoneFile = modcimfs.NewProc("CimTombstoneFile")
procCimWriteStream = modcimfs.NewProc("CimWriteStream")
procSetJobCompartmentId = modiphlpapi.NewProc("SetJobCompartmentId")
procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole")
@@ -181,6 +187,54 @@ func _CMLocateDevNode(pdnDevInst *uint32, pDeviceID *uint16, uFlags uint32) (hr
return
}
func CimAddFsToMergedImage(cimFSHandle FsHandle, path string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(path)
if hr != nil {
return
}
return _CimAddFsToMergedImage(cimFSHandle, _p0)
}
func _CimAddFsToMergedImage(cimFSHandle FsHandle, path *uint16) (hr error) {
hr = procCimAddFsToMergedImage.Find()
if hr != nil {
return
}
r0, _, _ := syscall.SyscallN(procCimAddFsToMergedImage.Addr(), uintptr(cimFSHandle), uintptr(unsafe.Pointer(path)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimAddFsToMergedImage2(cimFSHandle FsHandle, path string, flags uint32) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(path)
if hr != nil {
return
}
return _CimAddFsToMergedImage2(cimFSHandle, _p0, flags)
}
func _CimAddFsToMergedImage2(cimFSHandle FsHandle, path *uint16, flags uint32) (hr error) {
hr = procCimAddFsToMergedImage2.Find()
if hr != nil {
return
}
r0, _, _ := syscall.SyscallN(procCimAddFsToMergedImage2.Addr(), uintptr(cimFSHandle), uintptr(unsafe.Pointer(path)), uintptr(flags))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimCloseImage(cimFSHandle FsHandle) (err error) {
err = procCimCloseImage.Find()
if err != nil {
@@ -321,6 +375,59 @@ func _CimCreateImage(imagePath *uint16, oldFSName *uint16, newFSName *uint16, ci
return
}
func CimCreateImage2(imagePath string, flags uint32, oldFSName *uint16, newFSName *uint16, cimFSHandle *FsHandle) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(imagePath)
if hr != nil {
return
}
return _CimCreateImage2(_p0, flags, oldFSName, newFSName, cimFSHandle)
}
func _CimCreateImage2(imagePath *uint16, flags uint32, oldFSName *uint16, newFSName *uint16, cimFSHandle *FsHandle) (hr error) {
hr = procCimCreateImage2.Find()
if hr != nil {
return
}
r0, _, _ := syscall.SyscallN(procCimCreateImage2.Addr(), uintptr(unsafe.Pointer(imagePath)), uintptr(flags), uintptr(unsafe.Pointer(oldFSName)), uintptr(unsafe.Pointer(newFSName)), uintptr(unsafe.Pointer(cimFSHandle)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimCreateMergeLink(cimFSHandle FsHandle, newPath string, oldPath string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(newPath)
if hr != nil {
return
}
var _p1 *uint16
_p1, hr = syscall.UTF16PtrFromString(oldPath)
if hr != nil {
return
}
return _CimCreateMergeLink(cimFSHandle, _p0, _p1)
}
func _CimCreateMergeLink(cimFSHandle FsHandle, newPath *uint16, oldPath *uint16) (hr error) {
hr = procCimCreateMergeLink.Find()
if hr != nil {
return
}
r0, _, _ := syscall.SyscallN(procCimCreateMergeLink.Addr(), uintptr(cimFSHandle), uintptr(unsafe.Pointer(newPath)), uintptr(unsafe.Pointer(oldPath)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimDeletePath(cimFSHandle FsHandle, path string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(path)
@@ -360,6 +467,21 @@ func CimDismountImage(volumeID *g) (hr error) {
return
}
func CimMergeMountImage(numCimPaths uint32, backingImagePaths *CimFsImagePath, flags uint32, volumeID *g) (hr error) {
hr = procCimMergeMountImage.Find()
if hr != nil {
return
}
r0, _, _ := syscall.SyscallN(procCimMergeMountImage.Addr(), uintptr(numCimPaths), uintptr(unsafe.Pointer(backingImagePaths)), uintptr(flags), uintptr(unsafe.Pointer(volumeID)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimMountImage(imagePath string, fsName string, flags uint32, volumeID *g) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(imagePath)
@@ -389,6 +511,30 @@ func _CimMountImage(imagePath *uint16, fsName *uint16, flags uint32, volumeID *g
return
}
func CimTombstoneFile(cimFSHandle FsHandle, path string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(path)
if hr != nil {
return
}
return _CimTombstoneFile(cimFSHandle, _p0)
}
func _CimTombstoneFile(cimFSHandle FsHandle, path *uint16) (hr error) {
hr = procCimTombstoneFile.Find()
if hr != nil {
return
}
r0, _, _ := syscall.SyscallN(procCimTombstoneFile.Addr(), uintptr(cimFSHandle), uintptr(unsafe.Pointer(path)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimWriteStream(cimStreamHandle StreamHandle, buffer uintptr, bufferSize uint32) (hr error) {
hr = procCimWriteStream.Find()
if hr != nil {

View File

@@ -3,7 +3,8 @@ package osversion
// List of stable ABI compliant ltsc releases
// Note: List must be sorted in ascending order
var compatLTSCReleases = []uint16{
V21H2Server,
LTSC2022,
LTSC2025,
}
// CheckHostAndContainerCompat checks if given host and container
@@ -20,16 +21,25 @@ func CheckHostAndContainerCompat(host, ctr OSVersion) bool {
}
// If host is < WS 2022, exact version match is required
if host.Build < V21H2Server {
if host.Build < LTSC2022 {
return host.Build == ctr.Build
}
var supportedLtscRelease uint16
// Find the latest LTSC version that is earlier than the host version.
// This is the earliest version of container that the host can run.
//
// If the host version is an LTSC, then it supports compatibility with
// everything from the previous LTSC up to itself, so we want supportedLTSCRelease
// to be the previous entry.
//
// If no match is found, then we know that the host is LTSC2022 exactly,
// since we already checked that it's not less than LTSC2022.
var supportedLTSCRelease uint16 = LTSC2022
for i := len(compatLTSCReleases) - 1; i >= 0; i-- {
if host.Build >= compatLTSCReleases[i] {
supportedLtscRelease = compatLTSCReleases[i]
if host.Build > compatLTSCReleases[i] {
supportedLTSCRelease = compatLTSCReleases[i]
break
}
}
return ctr.Build >= supportedLtscRelease && ctr.Build <= host.Build
return supportedLTSCRelease <= ctr.Build && ctr.Build <= host.Build
}

View File

@@ -81,4 +81,11 @@ const (
// V22H2Win11 corresponds to Windows 11 (2022 Update).
V22H2Win11 = 22621
// V23H2 is the 23H2 release in the Windows Server annual channel.
V23H2 = 25398
// Windows Server 2025 build 26100
V25H1Server = 26100
LTSC2025 = V25H1Server
)

View File

@@ -1,291 +0,0 @@
//go:build windows
// +build windows
package cimfs
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"unsafe"
"github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/winapi"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
)
// CimFsWriter represents a writer to a single CimFS filesystem instance. On disk, the
// image is composed of a filesystem file and several object ID and region files.
// Note: The CimFsWriter isn't thread safe!
type CimFsWriter struct {
// name of this cim. Usually a <name>.cim file will be created to represent this cim.
name string
// handle is the CIMFS_IMAGE_HANDLE that must be passed when calling CIMFS APIs.
handle winapi.FsHandle
// name of the active file i.e the file to which we are currently writing.
activeName string
// stream to currently active file.
activeStream winapi.StreamHandle
// amount of bytes that can be written to the activeStream.
activeLeft uint64
}
// Create creates a new cim image. The CimFsWriter returned can then be used to do
// operations on this cim.
func Create(imagePath string, oldFSName string, newFSName string) (_ *CimFsWriter, err error) {
var oldNameBytes *uint16
// CimCreateImage API call has different behavior if the value of oldNameBytes / newNameBytes
// is empty than if it is nil. So we have to convert those strings into *uint16 here.
fsName := oldFSName
if oldFSName != "" {
oldNameBytes, err = windows.UTF16PtrFromString(oldFSName)
if err != nil {
return nil, err
}
}
var newNameBytes *uint16
if newFSName != "" {
fsName = newFSName
newNameBytes, err = windows.UTF16PtrFromString(newFSName)
if err != nil {
return nil, err
}
}
var handle winapi.FsHandle
if err := winapi.CimCreateImage(imagePath, oldNameBytes, newNameBytes, &handle); err != nil {
return nil, fmt.Errorf("failed to create cim image at path %s, oldName: %s, newName: %s: %w", imagePath, oldFSName, newFSName, err)
}
return &CimFsWriter{handle: handle, name: filepath.Join(imagePath, fsName)}, nil
}
// CreateAlternateStream creates alternate stream of given size at the given path inside the cim. This will
// replace the current active stream. Always, finish writing current active stream and then create an
// alternate stream.
func (c *CimFsWriter) CreateAlternateStream(path string, size uint64) (err error) {
err = c.closeStream()
if err != nil {
return err
}
err = winapi.CimCreateAlternateStream(c.handle, path, size, &c.activeStream)
if err != nil {
return fmt.Errorf("failed to create alternate stream for path %s: %w", path, err)
}
c.activeName = path
return nil
}
// closes the currently active stream.
func (c *CimFsWriter) closeStream() error {
if c.activeStream == 0 {
return nil
}
err := winapi.CimCloseStream(c.activeStream)
if err == nil && c.activeLeft > 0 {
// Validate here because CimCloseStream does not and this improves error
// reporting. Otherwise the error will occur in the context of
// cimWriteStream.
err = fmt.Errorf("incomplete write, %d bytes left in the stream %s", c.activeLeft, c.activeName)
}
if err != nil {
err = &PathError{Cim: c.name, Op: "closeStream", Path: c.activeName, Err: err}
}
c.activeLeft = 0
c.activeStream = 0
c.activeName = ""
return err
}
// AddFile adds a new file to the image. The file is added at the specified path. After
// calling this function, the file is set as the active stream for the image, so data can
// be written by calling `Write`.
func (c *CimFsWriter) AddFile(path string, info *winio.FileBasicInfo, fileSize int64, securityDescriptor []byte, extendedAttributes []byte, reparseData []byte) error {
err := c.closeStream()
if err != nil {
return err
}
fileMetadata := &winapi.CimFsFileMetadata{
Attributes: info.FileAttributes,
FileSize: fileSize,
CreationTime: info.CreationTime,
LastWriteTime: info.LastWriteTime,
ChangeTime: info.ChangeTime,
LastAccessTime: info.LastAccessTime,
}
if len(securityDescriptor) == 0 {
// Passing an empty security descriptor creates a CIM in a weird state.
// Pass the NULL DACL.
securityDescriptor = nullSd
}
fileMetadata.SecurityDescriptorBuffer = unsafe.Pointer(&securityDescriptor[0])
fileMetadata.SecurityDescriptorSize = uint32(len(securityDescriptor))
if len(reparseData) > 0 {
fileMetadata.ReparseDataBuffer = unsafe.Pointer(&reparseData[0])
fileMetadata.ReparseDataSize = uint32(len(reparseData))
}
if len(extendedAttributes) > 0 {
fileMetadata.ExtendedAttributes = unsafe.Pointer(&extendedAttributes[0])
fileMetadata.EACount = uint32(len(extendedAttributes))
}
// remove the trailing `\` if present, otherwise it trips off the cim writer
path = strings.TrimSuffix(path, "\\")
err = winapi.CimCreateFile(c.handle, path, fileMetadata, &c.activeStream)
if err != nil {
return &PathError{Cim: c.name, Op: "addFile", Path: path, Err: err}
}
c.activeName = path
if info.FileAttributes&(windows.FILE_ATTRIBUTE_DIRECTORY) == 0 {
c.activeLeft = uint64(fileSize)
}
return nil
}
// Write writes bytes to the active stream.
func (c *CimFsWriter) Write(p []byte) (int, error) {
if c.activeStream == 0 {
return 0, fmt.Errorf("no active stream")
}
if uint64(len(p)) > c.activeLeft {
return 0, &PathError{Cim: c.name, Op: "write", Path: c.activeName, Err: fmt.Errorf("wrote too much")}
}
err := winapi.CimWriteStream(c.activeStream, uintptr(unsafe.Pointer(&p[0])), uint32(len(p)))
if err != nil {
err = &PathError{Cim: c.name, Op: "write", Path: c.activeName, Err: err}
return 0, err
}
c.activeLeft -= uint64(len(p))
return len(p), nil
}
// AddLink adds a hard link from `oldPath` to `newPath` in the image.
func (c *CimFsWriter) AddLink(oldPath string, newPath string) error {
err := c.closeStream()
if err != nil {
return err
}
err = winapi.CimCreateHardLink(c.handle, newPath, oldPath)
if err != nil {
err = &LinkError{Cim: c.name, Op: "addLink", Old: oldPath, New: newPath, Err: err}
}
return err
}
// Unlink deletes the file at `path` from the image.
func (c *CimFsWriter) Unlink(path string) error {
err := c.closeStream()
if err != nil {
return err
}
//TODO(ambarve): CimDeletePath currently returns an error if the file isn't found but we ideally want
// to put a tombstone at that path so that when cims are merged it removes that file from the lower
// layer
err = winapi.CimDeletePath(c.handle, path)
if err != nil && !os.IsNotExist(err) {
err = &PathError{Cim: c.name, Op: "unlink", Path: path, Err: err}
return err
}
return nil
}
func (c *CimFsWriter) commit() error {
err := c.closeStream()
if err != nil {
return err
}
err = winapi.CimCommitImage(c.handle)
if err != nil {
err = &OpError{Cim: c.name, Op: "commit", Err: err}
}
return err
}
// Close closes the CimFS filesystem.
func (c *CimFsWriter) Close() error {
if c.handle == 0 {
return fmt.Errorf("invalid writer")
}
if err := c.commit(); err != nil {
return &OpError{Cim: c.name, Op: "commit", Err: err}
}
if err := winapi.CimCloseImage(c.handle); err != nil {
return &OpError{Cim: c.name, Op: "close", Err: err}
}
c.handle = 0
return nil
}
// DestroyCim finds out the region files, object files of this cim and then delete
// the region files, object files and the <layer-id>.cim file itself.
func DestroyCim(ctx context.Context, cimPath string) (retErr error) {
regionFilePaths, err := getRegionFilePaths(ctx, cimPath)
if err != nil {
log.G(ctx).WithError(err).Warnf("get region files for cim %s", cimPath)
if retErr == nil { //nolint:govet // nilness: consistency with below
retErr = err
}
}
objectFilePaths, err := getObjectIDFilePaths(ctx, cimPath)
if err != nil {
log.G(ctx).WithError(err).Warnf("get objectid file for cim %s", cimPath)
if retErr == nil {
retErr = err
}
}
log.G(ctx).WithFields(logrus.Fields{
"cimPath": cimPath,
"regionFiles": regionFilePaths,
"objectFiles": objectFilePaths,
}).Debug("destroy cim")
for _, regFilePath := range regionFilePaths {
if err := os.Remove(regFilePath); err != nil {
log.G(ctx).WithError(err).Warnf("remove file %s", regFilePath)
if retErr == nil {
retErr = err
}
}
}
for _, objFilePath := range objectFilePaths {
if err := os.Remove(objFilePath); err != nil {
log.G(ctx).WithError(err).Warnf("remove file %s", objFilePath)
if retErr == nil {
retErr = err
}
}
}
if err := os.Remove(cimPath); err != nil {
log.G(ctx).WithError(err).Warnf("remove file %s", cimPath)
if retErr == nil {
retErr = err
}
}
return retErr
}
// GetCimUsage returns the total disk usage in bytes by the cim at path `cimPath`.
func GetCimUsage(ctx context.Context, cimPath string) (uint64, error) {
regionFilePaths, err := getRegionFilePaths(ctx, cimPath)
if err != nil {
return 0, fmt.Errorf("get region file paths for cim %s: %w", cimPath, err)
}
objectFilePaths, err := getObjectIDFilePaths(ctx, cimPath)
if err != nil {
return 0, fmt.Errorf("get objectid file for cim %s: %w", cimPath, err)
}
var totalUsage uint64
for _, f := range append(regionFilePaths, objectFilePaths...) {
fi, err := os.Stat(f)
if err != nil {
return 0, fmt.Errorf("stat file %s: %w", f, err)
}
totalUsage += uint64(fi.Size())
}
return totalUsage, nil
}

View File

@@ -1,17 +0,0 @@
//go:build windows
// +build windows
package cimfs
import (
"github.com/Microsoft/hcsshim/osversion"
"github.com/sirupsen/logrus"
)
func IsCimFSSupported() bool {
rv, err := osversion.BuildRevision()
if err != nil {
logrus.WithError(err).Warn("get build revision")
}
return osversion.Build() == 20348 && rv >= 2031
}

View File

@@ -1,134 +0,0 @@
//go:build windows
// +build windows
package cimfs
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"os"
"path/filepath"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/pkg/cimfs/format"
)
var (
// Equivalent to SDDL of "D:NO_ACCESS_CONTROL".
nullSd = []byte{1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
)
type OpError struct {
Cim string
Op string
Err error
}
func (e *OpError) Error() string {
s := "cim " + e.Op + " " + e.Cim
s += ": " + e.Err.Error()
return s
}
// PathError is the error type returned by most functions in this package.
type PathError struct {
Cim string
Op string
Path string
Err error
}
func (e *PathError) Error() string {
s := "cim " + e.Op + " " + e.Cim
s += ":" + e.Path
s += ": " + e.Err.Error()
return s
}
type LinkError struct {
Cim string
Op string
Old string
New string
Err error
}
func (e *LinkError) Error() string {
return "cim " + e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}
func validateHeader(h *format.CommonHeader) error {
if !bytes.Equal(h.Magic[:], format.MagicValue[:]) {
return fmt.Errorf("not a cim file")
}
if h.Version.Major > format.CurrentVersion.Major || h.Version.Major < format.MinSupportedVersion.Major {
return fmt.Errorf("unsupported cim version. cim version %v must be between %v & %v", h.Version, format.MinSupportedVersion, format.CurrentVersion)
}
return nil
}
func readFilesystemHeader(f *os.File) (format.FilesystemHeader, error) {
var fsh format.FilesystemHeader
if err := binary.Read(f, binary.LittleEndian, &fsh); err != nil {
return fsh, fmt.Errorf("reading filesystem header: %w", err)
}
if err := validateHeader(&fsh.Common); err != nil {
return fsh, fmt.Errorf("validating filesystem header: %w", err)
}
return fsh, nil
}
// Returns the paths of all the objectID files associated with the cim at `cimPath`.
func getObjectIDFilePaths(ctx context.Context, cimPath string) ([]string, error) {
f, err := os.Open(cimPath)
if err != nil {
return []string{}, fmt.Errorf("open cim file %s: %w", cimPath, err)
}
defer f.Close()
fsh, err := readFilesystemHeader(f)
if err != nil {
return []string{}, fmt.Errorf("readingp cim header: %w", err)
}
paths := []string{}
for i := 0; i < int(fsh.Regions.Count); i++ {
path := filepath.Join(filepath.Dir(cimPath), fmt.Sprintf("%s_%v_%d", format.ObjectIDFileName, fsh.Regions.ID, i))
if _, err := os.Stat(path); err == nil {
paths = append(paths, path)
} else {
log.G(ctx).WithError(err).Warnf("stat for object file %s", path)
}
}
return paths, nil
}
// Returns the paths of all the region files associated with the cim at `cimPath`.
func getRegionFilePaths(ctx context.Context, cimPath string) ([]string, error) {
f, err := os.Open(cimPath)
if err != nil {
return []string{}, fmt.Errorf("open cim file %s: %w", cimPath, err)
}
defer f.Close()
fsh, err := readFilesystemHeader(f)
if err != nil {
return []string{}, fmt.Errorf("reading cim header: %w", err)
}
paths := []string{}
for i := 0; i < int(fsh.Regions.Count); i++ {
path := filepath.Join(filepath.Dir(cimPath), fmt.Sprintf("%s_%v_%d", format.RegionFileName, fsh.Regions.ID, i))
if _, err := os.Stat(path); err == nil {
paths = append(paths, path)
} else {
log.G(ctx).WithError(err).Warnf("stat for region file %s", path)
}
}
return paths, nil
}

View File

@@ -1,3 +0,0 @@
// This package provides simple go wrappers on top of the win32 CIMFS mount APIs.
// The mounting/unmount of cim layers is done by the cim mount functions the internal/wclayer/cim package.
package cimfs

View File

@@ -1,4 +0,0 @@
// format package maintains some basic structures to allows us to read header of a cim file. This is mostly
// required to understand the region & objectid files associated with a particular cim. Otherwise, we don't
// need to parse the cim format.
package format

View File

@@ -1,61 +0,0 @@
//go:build windows
// +build windows
package format
import "github.com/Microsoft/go-winio/pkg/guid"
const (
RegionFileName = "region"
ObjectIDFileName = "objectid"
)
// Magic specifies the magic number at the beginning of a file.
type Magic [8]uint8
var MagicValue = Magic([8]uint8{'c', 'i', 'm', 'f', 'i', 'l', 'e', '0'})
type Version struct {
Major, Minor uint32
}
var CurrentVersion = Version{3, 0}
var MinSupportedVersion = Version{2, 0}
type FileType uint8
// RegionOffset encodes an offset to objects as index of the region file
// containing the object and the byte offset within that file.
type RegionOffset uint64
// CommonHeader is the common header for all CIM-related files.
type CommonHeader struct {
Magic Magic
HeaderLength uint32
Type FileType
Reserved uint8
Reserved2 uint16
Version Version
Reserved3 uint64
}
type RegionSet struct {
ID guid.GUID
Count uint16
Reserved uint16
Reserved1 uint32
}
// FilesystemHeader is the header for a filesystem file.
//
// The filesystem file points to the filesystem object inside a region
// file and specifies regions sets.
type FilesystemHeader struct {
Common CommonHeader
Regions RegionSet
FilesystemOffset RegionOffset
Reserved uint32
Reserved1 uint16
ParentCount uint16
}

View File

@@ -1,65 +0,0 @@
//go:build windows
// +build windows
package cimfs
import (
"fmt"
"path/filepath"
"strings"
"github.com/Microsoft/go-winio/pkg/guid"
"github.com/Microsoft/hcsshim/internal/winapi"
"github.com/pkg/errors"
)
type MountError struct {
Cim string
Op string
VolumeGUID guid.GUID
Err error
}
func (e *MountError) Error() string {
s := "cim " + e.Op
if e.Cim != "" {
s += " " + e.Cim
}
s += " " + e.VolumeGUID.String() + ": " + e.Err.Error()
return s
}
// Mount mounts the given cim at a volume with given GUID. Returns the full volume
// path if mount is successful.
func Mount(cimPath string, volumeGUID guid.GUID, mountFlags uint32) (string, error) {
if err := winapi.CimMountImage(filepath.Dir(cimPath), filepath.Base(cimPath), mountFlags, &volumeGUID); err != nil {
return "", &MountError{Cim: cimPath, Op: "Mount", VolumeGUID: volumeGUID, Err: err}
}
return fmt.Sprintf("\\\\?\\Volume{%s}\\", volumeGUID.String()), nil
}
// Unmount unmounts the cim at mounted at path `volumePath`.
func Unmount(volumePath string) error {
// The path is expected to be in the \\?\Volume{GUID}\ format
if volumePath[len(volumePath)-1] != '\\' {
volumePath += "\\"
}
if !(strings.HasPrefix(volumePath, "\\\\?\\Volume{") && strings.HasSuffix(volumePath, "}\\")) {
return errors.Errorf("volume path %s is not in the expected format", volumePath)
}
trimmedStr := strings.TrimPrefix(volumePath, "\\\\?\\Volume{")
trimmedStr = strings.TrimSuffix(trimmedStr, "}\\")
volGUID, err := guid.FromString(trimmedStr)
if err != nil {
return errors.Wrapf(err, "guid parsing failed for %s", trimmedStr)
}
if err := winapi.CimDismountImage(&volGUID); err != nil {
return &MountError{VolumeGUID: volGUID, Op: "Unmount", Err: err}
}
return nil
}

View File

@@ -1,166 +0,0 @@
//go:build windows
// +build windows
package cim
import (
"archive/tar"
"bufio"
"context"
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"github.com/Microsoft/go-winio/backuptar"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/wclayer/cim"
"github.com/Microsoft/hcsshim/pkg/ociwclayer"
"golang.org/x/sys/windows"
)
// ImportCimLayerFromTar reads a layer from an OCI layer tar stream and extracts it into
// the CIM format at the specified path. The caller must specify the parent layers, if
// any, ordered from lowest to highest layer.
// This function expects that the layer paths (both the layer that is being imported & the parent layers) are
// formatted like `.../snapshots/<id>` and the corresponding layer CIMs are located/will be created at
// `.../snapshots/cim-layers/<id>.cim`. Each CIM file also has corresponding region & objectID files and those
// files will also be stored inside the `cim-layers` directory.
//
// This function returns the total size of the layer's files, in bytes.
func ImportCimLayerFromTar(ctx context.Context, r io.Reader, layerPath string, parentLayerPaths []string) (int64, error) {
err := os.MkdirAll(layerPath, 0)
if err != nil {
return 0, err
}
w, err := cim.NewCimLayerWriter(ctx, layerPath, parentLayerPaths)
if err != nil {
return 0, err
}
n, err := writeCimLayerFromTar(ctx, r, w, layerPath)
cerr := w.Close(ctx)
if err != nil {
return 0, err
}
if cerr != nil {
return 0, cerr
}
return n, nil
}
func writeCimLayerFromTar(ctx context.Context, r io.Reader, w *cim.CimLayerWriter, layerPath string) (int64, error) {
tr := tar.NewReader(r)
buf := bufio.NewWriter(w)
size := int64(0)
// Iterate through the files in the archive.
hdr, loopErr := tr.Next()
for loopErr == nil {
select {
case <-ctx.Done():
return 0, ctx.Err()
default:
}
// Note: path is used instead of filepath to prevent OS specific handling
// of the tar path
base := path.Base(hdr.Name)
if strings.HasPrefix(base, ociwclayer.WhiteoutPrefix) {
name := path.Join(path.Dir(hdr.Name), base[len(ociwclayer.WhiteoutPrefix):])
if rErr := w.Remove(filepath.FromSlash(name)); rErr != nil {
return 0, rErr
}
hdr, loopErr = tr.Next()
} else if hdr.Typeflag == tar.TypeLink {
if linkErr := w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)); linkErr != nil {
return 0, linkErr
}
hdr, loopErr = tr.Next()
} else {
name, fileSize, fileInfo, err := backuptar.FileInfoFromHeader(hdr)
if err != nil {
return 0, err
}
sddl, err := backuptar.SecurityDescriptorFromTarHeader(hdr)
if err != nil {
return 0, err
}
eadata, err := backuptar.ExtendedAttributesFromTarHeader(hdr)
if err != nil {
return 0, err
}
var reparse []byte
// As of now the only valid reparse data in a layer will be for a symlink. If file is
// a symlink set reparse attribute and ensure reparse data buffer isn't
// empty. Otherwise remove the reparse attributed.
fileInfo.FileAttributes &^= uint32(windows.FILE_ATTRIBUTE_REPARSE_POINT)
if hdr.Typeflag == tar.TypeSymlink {
reparse = backuptar.EncodeReparsePointFromTarHeader(hdr)
if len(reparse) > 0 {
fileInfo.FileAttributes |= uint32(windows.FILE_ATTRIBUTE_REPARSE_POINT)
}
}
if addErr := w.Add(filepath.FromSlash(name), fileInfo, fileSize, sddl, eadata, reparse); addErr != nil {
return 0, addErr
}
if hdr.Typeflag == tar.TypeReg {
if _, cpErr := io.Copy(buf, tr); cpErr != nil {
return 0, cpErr
}
}
size += fileSize
// Copy all the alternate data streams and return the next non-ADS header.
var ahdr *tar.Header
for {
ahdr, loopErr = tr.Next()
if loopErr != nil {
break
}
if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
hdr = ahdr
break
}
// stream names have following format: '<filename>:<stream name>:$DATA'
// $DATA is one of the valid types of streams. We currently only support
// data streams so fail if this is some other type of stream.
if !strings.HasSuffix(ahdr.Name, ":$DATA") {
return 0, fmt.Errorf("stream types other than $DATA are not supported, found: %s", ahdr.Name)
}
if addErr := w.AddAlternateStream(filepath.FromSlash(ahdr.Name), uint64(ahdr.Size)); addErr != nil {
return 0, addErr
}
if _, cpErr := io.Copy(buf, tr); cpErr != nil {
return 0, cpErr
}
}
}
if flushErr := buf.Flush(); flushErr != nil {
if loopErr == nil {
loopErr = flushErr
} else {
log.G(ctx).WithError(flushErr).Warn("flush buffer during layer write failed")
}
}
}
if !errors.Is(loopErr, io.EOF) {
return 0, loopErr
}
return size, nil
}
func DestroyCimLayer(layerPath string) error {
return cim.DestroyCimLayer(context.Background(), layerPath)
}

View File

@@ -61,8 +61,7 @@ func ImportLayerFromTar(ctx context.Context, r io.Reader, path string, parentLay
func writeLayerFromTar(ctx context.Context, r io.Reader, w wclayer.LayerWriter, root string) (int64, error) {
t := tar.NewReader(r)
// CodeQL [SM03409] False positive, `internal/safefile` package ensures tar extractions are always
// bound to the layer root directory.
// CodeQL [SM03409] `internal\wclayer` uses `internal/safefile` to bind tar extraction to the layer's root directory
hdr, err := t.Next()
totalSize := int64(0)
buf := bufio.NewWriter(nil)
@@ -80,16 +79,14 @@ func writeLayerFromTar(ctx context.Context, r io.Reader, w wclayer.LayerWriter,
if err != nil {
return 0, err
}
// CodeQL [SM03409] False positive, `internal/safefile` package ensures tar extractions are always
// bound to the layer root directory.
// CodeQL [SM03409] `internal\wclayer` uses `internal/safefile` to bind tar extraction to the layer's root directory
hdr, err = t.Next()
} else if hdr.Typeflag == tar.TypeLink {
err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname))
if err != nil {
return 0, err
}
// CodeQL [SM03409] False positive, `internal/safefile` package ensures tar extractions are always
// bound to the layer root directory.
// CodeQL [SM03409] `internal\wclayer` uses `internal/safefile` to bind tar extraction to the layer's root directory
hdr, err = t.Next()
} else {
var (

View File

@@ -37,6 +37,7 @@ import (
sandboxsapi "github.com/containerd/containerd/api/services/sandbox/v1"
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
"github.com/containerd/containerd/api/services/tasks/v1"
transferapi "github.com/containerd/containerd/api/services/transfer/v1"
versionservice "github.com/containerd/containerd/api/services/version/v1"
apitypes "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/v2/core/containers"
@@ -55,6 +56,8 @@ import (
sandboxproxy "github.com/containerd/containerd/v2/core/sandbox/proxy"
"github.com/containerd/containerd/v2/core/snapshots"
snproxy "github.com/containerd/containerd/v2/core/snapshots/proxy"
"github.com/containerd/containerd/v2/core/transfer"
transferproxy "github.com/containerd/containerd/v2/core/transfer/proxy"
"github.com/containerd/containerd/v2/defaults"
"github.com/containerd/containerd/v2/pkg/dialer"
"github.com/containerd/containerd/v2/pkg/namespaces"
@@ -140,6 +143,8 @@ func New(address string, opts ...Opt) (*Client, error) {
if len(copts.dialOptions) > 0 {
gopts = copts.dialOptions
}
gopts = append(gopts, copts.extraDialOpts...)
gopts = append(gopts, grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize),
grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)))
@@ -380,22 +385,26 @@ type RemoteContext struct {
// after it has completed transferring.
HandlerWrapper func(images.Handler) images.Handler
// ConvertSchema1 is whether to convert Docker registry schema 1
// manifests. If this option is false then any image which resolves
// to schema 1 will return an error since schema 1 is not supported.
//
// Deprecated: use Schema 2 or OCI images.
ConvertSchema1 bool
// Platforms defines which platforms to handle when doing the image operation.
// Platforms is ignored when a PlatformMatcher is set, otherwise the
// platforms will be used to create a PlatformMatcher with no ordering
// preference.
Platforms []string
// MaxConcurrentDownloads is the max concurrent content downloads for each pull.
DownloadLimiter *semaphore.Weighted
// MaxConcurrentDownloads restricts the total number of concurrent downloads
// across all layers during an image pull operation. This helps control the
// overall network bandwidth usage.
MaxConcurrentDownloads int
// ConcurrentLayerFetchBuffer sets the maximum size in bytes for each chunk
// when downloading layers in parallel. Larger chunks reduce coordination
// overhead but use more memory. When ConcurrentLayerFetchBuffer is above
// 512 bytes, parallel layer fetch is enabled. It can accelerate pulls for
// big images.
ConcurrentLayerFetchBuffer int
// MaxConcurrentUploadedLayers is the max concurrent uploaded layers for each push.
MaxConcurrentUploadedLayers int
@@ -757,6 +766,16 @@ func (c *Client) SandboxController(name string) sandbox.Controller {
return sandboxproxy.NewSandboxController(sandboxsapi.NewControllerClient(c.conn), name)
}
// TranferService returns the underlying transferrer
func (c *Client) TransferService() transfer.Transferrer {
if c.transferService != nil {
return c.transferService
}
c.connMu.Lock()
defer c.connMu.Unlock()
return transferproxy.NewTransferrer(transferapi.NewTransferClient(c.conn), c.streamCreator())
}
// VersionService returns the underlying VersionClient
func (c *Client) VersionService() versionservice.VersionClient {
c.connMu.Lock()

View File

@@ -24,6 +24,7 @@ import (
"github.com/containerd/containerd/v2/core/snapshots"
"github.com/containerd/platforms"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/sync/semaphore"
"google.golang.org/grpc"
)
@@ -34,6 +35,7 @@ type clientOpts struct {
defaultPlatform platforms.MatchComparer
services *services
dialOptions []grpc.DialOption
extraDialOpts []grpc.DialOption
callOptions []grpc.CallOption
timeout time.Duration
}
@@ -76,6 +78,19 @@ func WithDialOpts(opts []grpc.DialOption) Opt {
}
}
// WithExtraDialOpts allows additional grpc.DialOptions to be set on the
// connection. Unlike [WithDialOpts], options set here are appended to,
// instead of overriding previous options, which allows setting options
// to extend containerd client's defaults.
//
// This option can be used multiple times to set additional dial options.
func WithExtraDialOpts(opts []grpc.DialOption) Opt {
return func(c *clientOpts) error {
c.extraDialOpts = append(c.extraDialOpts, opts...)
return nil
}
}
// WithCallOpts allows grpc.CallOptions to be set on the connection
func WithCallOpts(opts []grpc.CallOption) Opt {
return func(c *clientOpts) error {
@@ -197,16 +212,6 @@ func WithChildLabelMap(fn func(ocispec.Descriptor) []string) RemoteOpt {
}
}
// WithSchema1Conversion is used to convert Docker registry schema 1
// manifests to oci manifests on pull. Without this option schema 1
// manifests will return a not supported error.
//
// Deprecated: use Schema 2 or OCI images.
func WithSchema1Conversion(client *Client, c *RemoteContext) error {
c.ConvertSchema1 = true
return nil
}
// WithResolver specifies the resolver to use.
func WithResolver(resolver remotes.Resolver) RemoteOpt {
return func(client *Client, c *RemoteContext) error {
@@ -231,6 +236,14 @@ func WithImageHandlerWrapper(w func(images.Handler) images.Handler) RemoteOpt {
}
}
// WithDownloadLimiter sets the limiter for concurrent download operations.
func WithDownloadLimiter(limiter *semaphore.Weighted) RemoteOpt {
return func(client *Client, c *RemoteContext) error {
c.DownloadLimiter = limiter
return nil
}
}
// WithMaxConcurrentDownloads sets max concurrent download limit.
func WithMaxConcurrentDownloads(max int) RemoteOpt {
return func(client *Client, c *RemoteContext) error {
@@ -239,6 +252,14 @@ func WithMaxConcurrentDownloads(max int) RemoteOpt {
}
}
// WithConcurrentLayerFetchBuffer sets the buffer size for concurrent layer fetches.
func WithConcurrentLayerFetchBuffer(buffer int) RemoteOpt {
return func(client *Client, c *RemoteContext) error {
c.ConcurrentLayerFetchBuffer = buffer
return nil
}
}
// WithMaxConcurrentUploadedLayers sets max concurrent uploaded layer limit.
func WithMaxConcurrentUploadedLayers(max int) RemoteOpt {
return func(client *Client, c *RemoteContext) error {

View File

@@ -85,6 +85,9 @@ type Container interface {
Update(context.Context, ...UpdateContainerOpts) error
// Checkpoint creates a checkpoint image of the current container
Checkpoint(context.Context, string, ...CheckpointOpts) (Image, error)
// Restore restores a container and returns the PID of the
// restored containers init process.
Restore(context.Context, cio.Creator, string) (int, error)
}
func containerFromRecord(client *Client, c containers.Container) *container {
@@ -221,7 +224,7 @@ func (c *container) Image(ctx context.Context) (Image, error) {
return NewImage(c.client, i), nil
}
func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...NewTaskOpts) (_ Task, err error) {
func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...NewTaskOpts) (_ Task, retErr error) {
ctx, span := tracing.StartSpan(ctx, "container.NewTask")
defer span.End()
i, err := ioCreate(c.id)
@@ -229,7 +232,7 @@ func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...N
return nil, err
}
defer func() {
if err != nil && i != nil {
if retErr != nil && i != nil {
i.Cancel()
i.Close()
}
@@ -242,42 +245,14 @@ func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...N
Stdout: cfg.Stdout,
Stderr: cfg.Stderr,
}
if err := c.handleMounts(ctx, request); err != nil {
return nil, err
}
r, err := c.get(ctx)
if err != nil {
return nil, err
}
if r.SnapshotKey != "" {
if r.Snapshotter == "" {
return nil, fmt.Errorf("unable to resolve rootfs mounts without snapshotter on container: %w", errdefs.ErrInvalidArgument)
}
// get the rootfs from the snapshotter and add it to the request
s, err := c.client.getSnapshotter(ctx, r.Snapshotter)
if err != nil {
return nil, err
}
mounts, err := s.Mounts(ctx, r.SnapshotKey)
if err != nil {
return nil, err
}
spec, err := c.Spec(ctx)
if err != nil {
return nil, err
}
for _, m := range mounts {
if spec.Linux != nil && spec.Linux.MountLabel != "" {
if ml := label.FormatMountLabel("", spec.Linux.MountLabel); ml != "" {
m.Options = append(m.Options, ml)
}
}
request.Rootfs = append(request.Rootfs, &types.Mount{
Type: m.Type,
Source: m.Source,
Target: m.Target,
Options: m.Options,
})
}
}
info := TaskInfo{
runtime: r.Runtime.Name,
runtimeOptions: r.Runtime.Options,
@@ -349,6 +324,94 @@ func (c *container) Update(ctx context.Context, opts ...UpdateContainerOpts) err
return nil
}
func (c *container) handleMounts(ctx context.Context, request *tasks.CreateTaskRequest) error {
r, err := c.get(ctx)
if err != nil {
return err
}
if r.SnapshotKey != "" {
if r.Snapshotter == "" {
return fmt.Errorf("unable to resolve rootfs mounts without snapshotter on container: %w", errdefs.ErrInvalidArgument)
}
// get the rootfs from the snapshotter and add it to the request
s, err := c.client.getSnapshotter(ctx, r.Snapshotter)
if err != nil {
return err
}
mounts, err := s.Mounts(ctx, r.SnapshotKey)
if err != nil {
return err
}
spec, err := c.Spec(ctx)
if err != nil {
return err
}
for _, m := range mounts {
if spec.Linux != nil && spec.Linux.MountLabel != "" {
if ml := label.FormatMountLabel("", spec.Linux.MountLabel); ml != "" {
m.Options = append(m.Options, ml)
}
}
request.Rootfs = append(request.Rootfs, &types.Mount{
Type: m.Type,
Source: m.Source,
Target: m.Target,
Options: m.Options,
})
}
}
return nil
}
func (c *container) Restore(ctx context.Context, ioCreate cio.Creator, rootDir string) (_ int, retErr error) {
errorPid := -1
i, err := ioCreate(c.id)
if err != nil {
return errorPid, err
}
defer func() {
if retErr != nil && i != nil {
i.Cancel()
i.Close()
}
}()
cfg := i.Config()
request := &tasks.CreateTaskRequest{
ContainerID: c.id,
Terminal: cfg.Terminal,
Stdin: cfg.Stdin,
Stdout: cfg.Stdout,
Stderr: cfg.Stderr,
}
if err := c.handleMounts(ctx, request); err != nil {
return errorPid, err
}
request.Checkpoint = &types.Descriptor{
Annotations: map[string]string{
// The following annotation is used to restore a checkpoint
// via CRI. This is mainly used to restore a container
// in Kubernetes.
"RestoreFromPath": rootDir,
},
}
// (adrianreber): it is not totally clear to me, but it seems the only
// way to restore a container in containerd is going through Create().
// This functions sets up Create() in such a way to handle container
// restore coming through the CRI.
response, err := c.client.TaskService().Create(ctx, request)
if err != nil {
return errorPid, errgrpc.ToNative(err)
}
return int(response.GetPid()), nil
}
func (c *container) Checkpoint(ctx context.Context, ref string, opts ...CheckpointOpts) (Image, error) {
index := &ocispec.Index{
Versioned: ver.Versioned{

View File

@@ -181,7 +181,6 @@ func containerFromProto(containerpb *containersapi.Container) containers.Contain
}
extensions := make(map[string]typeurl.Any)
for k, v := range containerpb.Extensions {
v := v
extensions[k] = v
}
return containers.Container{
@@ -203,7 +202,6 @@ func containersFromProto(containerspb []*containersapi.Container) []containers.C
var containers []containers.Container
for _, container := range containerspb {
container := container
containers = append(containers, containerFromProto(container))
}

View File

@@ -142,7 +142,6 @@ func imagesFromProto(imagespb []*imagesapi.Image) []images.Image {
var images []images.Image
for _, image := range imagespb {
image := image
images = append(images, imageFromProto(image))
}

View File

@@ -1,26 +0,0 @@
//go:build gofuzz
/*
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 client
import (
"github.com/AdamKorcz/go-118-fuzz-build/testing"
)
// To keep this package in go.mod.
var _ = testing.F{}

View File

@@ -27,7 +27,7 @@ import (
"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/core/remotes/docker/schema1" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
"github.com/containerd/containerd/v2/core/transfer"
"github.com/containerd/containerd/v2/core/unpack"
"github.com/containerd/containerd/v2/pkg/tracing"
"github.com/containerd/errdefs"
@@ -52,6 +52,14 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
}
}
if resolver, ok := pullCtx.Resolver.(remotes.ResolverWithOptions); ok {
resolver.SetOptions(
transfer.WithConcurrentLayerFetchBuffer(pullCtx.ConcurrentLayerFetchBuffer),
transfer.WithMaxConcurrentDownloads(pullCtx.MaxConcurrentDownloads),
transfer.WithDownloadLimiter(pullCtx.DownloadLimiter),
)
}
if pullCtx.PlatformMatcher == nil {
if len(pullCtx.Platforms) > 1 {
return nil, errors.New("cannot pull multiplatform image locally, try Fetch")
@@ -71,6 +79,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
tracing.Attribute("image.ref", ref),
tracing.Attribute("unpack", pullCtx.Unpack),
tracing.Attribute("max.concurrent.downloads", pullCtx.MaxConcurrentDownloads),
tracing.Attribute("concurrent.layer.fetch.buffer", pullCtx.ConcurrentLayerFetchBuffer),
tracing.Attribute("platforms.count", len(pullCtx.Platforms)),
)
@@ -109,9 +118,6 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
ApplyOpts: uconfig.ApplyOpts,
}
uopts := []unpack.UnpackerOpt{unpack.WithUnpackPlatform(platform)}
if pullCtx.MaxConcurrentDownloads > 0 {
uopts = append(uopts, unpack.WithLimiter(semaphore.NewWeighted(int64(pullCtx.MaxConcurrentDownloads))))
}
if uconfig.DuplicationSuppressor != nil {
uopts = append(uopts, unpack.WithDuplicationSuppressor(uconfig.DuplicationSuppressor))
}
@@ -164,7 +170,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
if unpacker != nil && ur.Unpacks == 0 {
// Unpack was tried previously but nothing was unpacked
// This is at least required for schema 1 image.
// This was at least required for schema 1 image.
if err := i.Unpack(ctx, pullCtx.Snapshotter, pullCtx.UnpackOpts...); err != nil {
return nil, fmt.Errorf("failed to unpack image on snapshotter %s: %w", pullCtx.Snapshotter, err)
}
@@ -190,83 +196,65 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
var (
handler images.Handler
isConvertible bool
originalSchema1Digest string
converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error)
limiter *semaphore.Weighted
isConvertible bool
converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error)
limiter *semaphore.Weighted
)
if desc.MediaType == images.MediaTypeDockerSchema1Manifest {
return images.Image{}, fmt.Errorf("%w: media type %q is no longer supported since containerd v2.1, please rebuild the image as %q or %q",
errdefs.ErrNotImplemented,
images.MediaTypeDockerSchema1Manifest, images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest)
}
// Get all the children for a descriptor
childrenHandler := images.ChildrenHandler(store)
// Set any children labels for that content
childrenHandler = images.SetChildrenMappedLabels(store, childrenHandler, rCtx.ChildLabelMap)
if rCtx.AllMetadata {
// Filter manifests by platforms but allow to handle manifest
// and configuration for not-target platforms
childrenHandler = remotes.FilterManifestByPlatformHandler(childrenHandler, rCtx.PlatformMatcher)
} else {
// Filter children by platforms if specified.
childrenHandler = images.FilterPlatforms(childrenHandler, rCtx.PlatformMatcher)
}
// Sort and limit manifests if a finite number is needed
if limit > 0 {
childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit)
}
// set isConvertible to true if there is application/octet-stream media type
convertibleHandler := images.HandlerFunc(
func(_ context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.MediaType == docker.LegacyConfigMediaType {
isConvertible = true
}
return []ocispec.Descriptor{}, nil
},
)
if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 {
schema1Converter, err := schema1.NewConverter(store, fetcher)
if err != nil {
return images.Image{}, fmt.Errorf("failed to get converter for %q: %w", ref, err)
}
appendDistSrcLabelHandler, err := docker.AppendDistributionSourceLabel(store, ref)
if err != nil {
return images.Image{}, err
}
handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...)
handlers := append(rCtx.BaseHandlers,
remotes.FetchHandler(store, fetcher),
convertibleHandler,
childrenHandler,
appendDistSrcLabelHandler,
)
isConvertible = true
handler = images.Handlers(handlers...)
converterFunc = func(ctx context.Context, _ ocispec.Descriptor) (ocispec.Descriptor, error) {
return schema1Converter.Convert(ctx)
}
originalSchema1Digest = desc.Digest.String()
} else {
// Get all the children for a descriptor
childrenHandler := images.ChildrenHandler(store)
// Set any children labels for that content
childrenHandler = images.SetChildrenMappedLabels(store, childrenHandler, rCtx.ChildLabelMap)
if rCtx.AllMetadata {
// Filter manifests by platforms but allow to handle manifest
// and configuration for not-target platforms
childrenHandler = remotes.FilterManifestByPlatformHandler(childrenHandler, rCtx.PlatformMatcher)
} else {
// Filter children by platforms if specified.
childrenHandler = images.FilterPlatforms(childrenHandler, rCtx.PlatformMatcher)
}
// Sort and limit manifests if a finite number is needed
if limit > 0 {
childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit)
}
// set isConvertible to true if there is application/octet-stream media type
convertibleHandler := images.HandlerFunc(
func(_ context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.MediaType == docker.LegacyConfigMediaType {
isConvertible = true
}
return []ocispec.Descriptor{}, nil
},
)
appendDistSrcLabelHandler, err := docker.AppendDistributionSourceLabel(store, ref)
if err != nil {
return images.Image{}, err
}
handlers := append(rCtx.BaseHandlers,
remotes.FetchHandler(store, fetcher),
convertibleHandler,
childrenHandler,
appendDistSrcLabelHandler,
)
handler = images.Handlers(handlers...)
converterFunc = func(ctx context.Context, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
return docker.ConvertManifest(ctx, store, desc)
}
converterFunc = func(ctx context.Context, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
return docker.ConvertManifest(ctx, store, desc)
}
if rCtx.HandlerWrapper != nil {
handler = rCtx.HandlerWrapper(handler)
}
if rCtx.MaxConcurrentDownloads > 0 {
limiter = semaphore.NewWeighted(int64(rCtx.MaxConcurrentDownloads))
}
if err := images.Dispatch(ctx, handler, limiter, desc); err != nil {
return images.Image{}, err
}
@@ -277,13 +265,6 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
}
}
if originalSchema1Digest != "" {
if rCtx.Labels == nil {
rCtx.Labels = make(map[string]string)
}
rCtx.Labels[images.ConvertedDockerSchema1LabelKey] = originalSchema1Digest
}
return images.Image{
Name: name,
Target: desc,

View File

@@ -31,6 +31,7 @@ import (
"github.com/containerd/containerd/v2/core/leases"
"github.com/containerd/containerd/v2/core/sandbox"
"github.com/containerd/containerd/v2/core/snapshots"
"github.com/containerd/containerd/v2/core/transfer"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/plugins"
srv "github.com/containerd/containerd/v2/plugins/services"
@@ -50,6 +51,7 @@ type services struct {
introspectionService introspection.Service
sandboxStore sandbox.Store
sandboxers map[string]sandbox.Controller
transferService transfer.Transferrer
}
// ServicesOpt allows callers to set options on the services
@@ -163,6 +165,13 @@ func WithSandboxStore(client sandbox.Store) ServicesOpt {
}
}
// WithTransferService sets the transfer service.
func WithTransferService(tr transfer.Transferrer) ServicesOpt {
return func(s *services) {
s.transferService = tr
}
}
// WithInMemoryServices is suitable for cases when there is need to use containerd's client from
// another (in-memory) containerd plugin (such as CRI).
func WithInMemoryServices(ic *plugin.InitContext) Opt {
@@ -178,6 +187,9 @@ func WithInMemoryServices(ic *plugin.InitContext) Opt {
plugins.SandboxStorePlugin: func(i interface{}) ServicesOpt {
return WithSandboxStore(i.(sandbox.Store))
},
plugins.TransferPlugin: func(i interface{}) ServicesOpt {
return WithTransferService(i.(transfer.Transferrer))
},
} {
i, err := ic.GetSingle(t)
if err != nil {

View File

@@ -39,9 +39,24 @@ const (
// to shift the filesystem ownership (user namespace mapping) automatically; currently
// supported by the fuse-overlayfs and overlay snapshotters
func WithRemapperLabels(ctrUID, hostUID, ctrGID, hostGID, length uint32) snapshots.Opt {
uidMap := []specs.LinuxIDMapping{{ContainerID: ctrUID, HostID: hostUID, Size: length}}
gidMap := []specs.LinuxIDMapping{{ContainerID: ctrGID, HostID: hostGID, Size: length}}
return WithUserNSRemapperLabels(uidMap, gidMap)
}
// WithUserNSRemapperLabels creates the labels used by any supporting snapshotter
// to shift the filesystem ownership (user namespace mapping) automatically; currently
// supported by the fuse-overlayfs and overlay snapshotters
func WithUserNSRemapperLabels(uidmaps, gidmaps []specs.LinuxIDMapping) snapshots.Opt {
idMap := userns.IDMap{
UidMap: uidmaps,
GidMap: gidmaps,
}
uidmapLabel, gidmapLabel := idMap.Marshal()
return snapshots.WithLabels(map[string]string{
snapshots.LabelSnapshotUIDMapping: fmt.Sprintf("%d:%d:%d", ctrUID, hostUID, length),
snapshots.LabelSnapshotGIDMapping: fmt.Sprintf("%d:%d:%d", ctrGID, hostGID, length)})
snapshots.LabelSnapshotUIDMapping: uidmapLabel,
snapshots.LabelSnapshotGIDMapping: gidmapLabel,
})
}
func resolveSnapshotOptions(ctx context.Context, client *Client, snapshotterName string, snapshotter snapshots.Snapshotter, parent string, opts ...snapshots.Opt) (string, error) {
@@ -89,27 +104,15 @@ func resolveSnapshotOptions(ctx context.Context, client *Client, snapshotterName
return "", fmt.Errorf("snapshotter %q doesn't support idmap mounts on this host, configure `slow_chown` to allow a slower and expensive fallback", snapshotterName)
}
var uidMap, gidMap specs.LinuxIDMapping
_, err = fmt.Sscanf(uidMapLabel, "%d:%d:%d", &uidMap.ContainerID, &uidMap.HostID, &uidMap.Size)
if err != nil {
return "", fmt.Errorf("uidMapLabel unparsable: %w", err)
}
_, err = fmt.Sscanf(gidMapLabel, "%d:%d:%d", &gidMap.ContainerID, &gidMap.HostID, &gidMap.Size)
if err != nil {
return "", fmt.Errorf("gidMapLabel unparsable: %w", err)
rsn := remappedSnapshot{Parent: parent}
if err = rsn.IDMap.Unmarshal(uidMapLabel, gidMapLabel); err != nil {
return "", fmt.Errorf("failed to unmarshal uid/gid map snapshotter labels: %w", err)
}
if uidMap.ContainerID != 0 || gidMap.ContainerID != 0 {
return "", fmt.Errorf("Container UID/GID of 0 only supported currently (%d/%d)", uidMap.ContainerID, gidMap.ContainerID)
if _, err := rsn.IDMap.RootPair(); err != nil {
return "", fmt.Errorf("container UID/GID mapping entries of 0 are required but not found")
}
rsn := remappedSnapshot{
Parent: parent,
IDMap: userns.IDMap{
UidMap: []specs.LinuxIDMapping{uidMap},
GidMap: []specs.LinuxIDMapping{gidMap},
},
}
usernsID, err := rsn.ID()
if err != nil {
return "", fmt.Errorf("failed to remap snapshot: %w", err)

View File

@@ -416,7 +416,7 @@ func (t *task) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitStat
return &ExitStatus{code: r.ExitStatus, exitedAt: protobuf.FromTimestamp(r.ExitedAt)}, nil
}
func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreate cio.Creator) (_ Process, err error) {
func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreate cio.Creator) (_ Process, retErr error) {
ctx, span := tracing.StartSpan(ctx, "task.Exec",
tracing.WithAttribute("task.id", t.ID()),
)
@@ -430,7 +430,7 @@ func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreat
return nil, err
}
defer func() {
if err != nil && i != nil {
if retErr != nil && i != nil {
i.Cancel()
i.Close()
}

View File

@@ -61,10 +61,6 @@ type Config struct {
State string `toml:"state"`
// TempDir is the path to a directory where to place containerd temporary files
TempDir string `toml:"temp"`
// PluginDir is the directory for dynamic plugins to be stored
//
// Deprecated: Please use proxy or binary external plugins.
PluginDir string `toml:"plugin_dir"`
// GRPC configuration settings
GRPC GRPCConfig `toml:"grpc"`
// TTRPC configuration settings

View File

@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"io"
"slices"
contentapi "github.com/containerd/containerd/api/services/content/v1"
"github.com/containerd/errdefs/pkg/errgrpc"
@@ -78,15 +79,9 @@ func (rw *remoteWriter) Digest() digest.Digest {
func (rw *remoteWriter) Write(p []byte) (n int, err error) {
const maxBufferSize = defaults.DefaultMaxSendMsgSize >> 1
for i := 0; i < len(p); i += maxBufferSize {
for data := range slices.Chunk(p, maxBufferSize) {
offset := rw.offset
end := i + maxBufferSize
if end > len(p) {
end = len(p)
}
data := p[i:end]
resp, err := rw.send(&contentapi.WriteContentRequest{
Action: contentapi.WriteAction_WRITE,
Offset: offset,

View File

@@ -165,8 +165,6 @@ func (c *defaultConverter) convertManifest(ctx context.Context, cs content.Store
var mu sync.Mutex
eg, ctx2 := errgroup.WithContext(ctx)
for i, l := range manifest.Layers {
i := i
l := l
oldDiffID, err := images.GetDiffID(ctx, cs, l)
if err != nil {
return nil, err
@@ -249,8 +247,6 @@ func (c *defaultConverter) convertIndex(ctx context.Context, cs content.Store, d
var mu sync.Mutex
eg, ctx2 := errgroup.WithContext(ctx)
for i, mani := range index.Manifests {
i := i
mani := mani
labelKey := fmt.Sprintf("containerd.io/gc.ref.content.m.%d", i)
eg.Go(func() error {
if mani.Platform != nil && !c.platformMC.Match(*mani.Platform) {

View File

@@ -154,8 +154,6 @@ func WalkNotEmpty(ctx context.Context, handler Handler, descs ...ocispec.Descrip
func Dispatch(ctx context.Context, handler Handler, limiter *semaphore.Weighted, descs ...ocispec.Descriptor) error {
eg, ctx2 := errgroup.WithContext(ctx)
for _, desc := range descs {
desc := desc
if limiter != nil {
if err := limiter.Acquire(ctx, 1); err != nil {
return err

View File

@@ -1,21 +0,0 @@
/*
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
const (
ConvertedDockerSchema1LabelKey = "io.containerd.image/converted-docker-schema1"
)

View File

@@ -85,7 +85,7 @@ func CalculateImageUsage(ctx context.Context, i images.Image, provider content.I
var (
handler = images.ChildrenHandler(provider)
size int64
size atomic.Int64
mustExist bool
)
@@ -153,7 +153,7 @@ func CalculateImageUsage(ctx context.Context, i images.Image, provider content.I
usage += desc.Size
}
atomic.AddInt64(&size, usage)
size.Add(usage)
return children, nil
}
@@ -163,5 +163,5 @@ func CalculateImageUsage(ctx context.Context, i images.Image, provider content.I
return 0, err
}
return size, nil
return size.Load(), nil
}

View File

@@ -21,6 +21,7 @@ import (
"fmt"
bolt "go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
)
type transactionKey struct{}
@@ -56,7 +57,7 @@ func update(ctx context.Context, db Transactor, fn func(*bolt.Tx) error) error {
if !ok {
return db.Update(fn)
} else if !tx.Writable() {
return fmt.Errorf("unable to use transaction from context: %w", bolt.ErrTxNotWritable)
return fmt.Errorf("unable to use transaction from context: %w", errbolt.ErrTxNotWritable)
}
return fn(tx)
}

View File

@@ -20,7 +20,6 @@ import (
"context"
"fmt"
"strings"
"sync/atomic"
"time"
"github.com/containerd/containerd/v2/core/containers"
@@ -35,6 +34,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/typeurl/v2"
bolt "go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
)
const (
@@ -143,7 +143,7 @@ func (s *containerStore) Create(ctx context.Context, container containers.Contai
cbkt, err := bkt.CreateBucket([]byte(container.ID))
if err != nil {
if err == bolt.ErrBucketExists {
if err == errbolt.ErrBucketExists {
err = fmt.Errorf("container %q: %w", container.ID, errdefs.ErrAlreadyExists)
}
return err
@@ -294,13 +294,13 @@ func (s *containerStore) Delete(ctx context.Context, id string) error {
}
if err := bkt.DeleteBucket([]byte(id)); err != nil {
if err == bolt.ErrBucketNotFound {
if err == errbolt.ErrBucketNotFound {
err = fmt.Errorf("container %v: %w", id, errdefs.ErrNotFound)
}
return err
}
atomic.AddUint32(&s.db.dirty, 1)
s.db.dirty.Add(1)
return nil
})

View File

@@ -22,14 +22,15 @@ import (
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/errdefs"
"github.com/containerd/log"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
bolt "go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/metadata/boltutil"
@@ -209,7 +210,7 @@ func (cs *contentStore) Delete(ctx context.Context, dgst digest.Digest) error {
cs.l.RLock()
defer cs.l.RUnlock()
return update(ctx, cs.db, func(tx *bolt.Tx) error {
if err := update(ctx, cs.db, func(tx *bolt.Tx) error {
bkt := getBlobBucket(tx, ns, dgst)
if bkt == nil {
return fmt.Errorf("content digest %v: %w", dgst, errdefs.ErrNotFound)
@@ -223,11 +224,22 @@ func (cs *contentStore) Delete(ctx context.Context, dgst digest.Digest) error {
}
// Mark content store as dirty for triggering garbage collection
atomic.AddUint32(&cs.db.dirty, 1)
cs.db.dirty.Add(1)
cs.db.dirtyCS = true
return nil
})
}); err != nil {
return err
}
if publisher := cs.db.Publisher(ctx); publisher != nil {
if err := publisher.Publish(ctx, "/content/delete", &eventstypes.ContentDelete{
Digest: dgst.String(),
}); err != nil {
return err
}
}
return nil
}
func (cs *contentStore) ListStatuses(ctx context.Context, fs ...string) ([]content.Status, error) {
@@ -488,7 +500,7 @@ type namespacedWriter struct {
ctx context.Context
ref string
namespace string
db Transactor
db *DB
provider interface {
content.Provider
content.Ingester
@@ -574,6 +586,8 @@ func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected dig
defer nw.l.RUnlock()
var innerErr error
var actualDgst digest.Digest
var actualSize int64
// We pre-sync the in-flight writes to the disk. This avoids the
// subsequent fp.Sync() call[1] from taking too long (10s+) while
@@ -588,16 +602,18 @@ func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected dig
}
if err := update(ctx, nw.db, func(tx *bolt.Tx) error {
dgst, err := nw.commit(ctx, tx, size, expected, opts...)
dgst, size, err := nw.commit(ctx, tx, size, expected, opts...)
if err != nil {
if !errdefs.IsAlreadyExists(err) {
return err
}
innerErr = err
}
actualDgst = dgst
actualSize = size
bkt := getIngestsBucket(tx, nw.namespace)
if bkt != nil {
if err := bkt.DeleteBucket([]byte(nw.ref)); err != nil && err != bolt.ErrBucketNotFound {
if err := bkt.DeleteBucket([]byte(nw.ref)); err != nil && err != errbolt.ErrBucketNotFound {
return err
}
}
@@ -608,8 +624,20 @@ func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected dig
}); err != nil {
return err
}
if innerErr != nil {
return innerErr
}
return innerErr
if publisher := nw.db.Publisher(ctx); publisher != nil {
if err := publisher.Publish(ctx, "/content/create", &eventstypes.ContentCreate{
Digest: actualDgst.String(),
Size: actualSize,
}); err != nil {
return err
}
}
return nil
}
func (nw *namespacedWriter) Sync() error {
@@ -619,30 +647,30 @@ func (nw *namespacedWriter) Sync() error {
return nil
}
func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, expected digest.Digest, opts ...content.Opt) (digest.Digest, error) {
func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, expected digest.Digest, opts ...content.Opt) (digest.Digest, int64, error) {
var base content.Info
for _, opt := range opts {
if err := opt(&base); err != nil {
if nw.w != nil {
nw.w.Close()
}
return "", err
return "", 0, err
}
}
if err := validateInfo(&base); err != nil {
if nw.w != nil {
nw.w.Close()
}
return "", err
return "", 0, err
}
var actual digest.Digest
if nw.w == nil {
if size != 0 && size != nw.desc.Size {
return "", fmt.Errorf("%q failed size validation: %v != %v: %w", nw.ref, nw.desc.Size, size, errdefs.ErrFailedPrecondition)
return "", 0, fmt.Errorf("%q failed size validation: %v != %v: %w", nw.ref, nw.desc.Size, size, errdefs.ErrFailedPrecondition)
}
if expected != "" && expected != nw.desc.Digest {
return "", fmt.Errorf("%q unexpected digest: %w", nw.ref, errdefs.ErrFailedPrecondition)
return "", 0, fmt.Errorf("%q unexpected digest: %w", nw.ref, errdefs.ErrFailedPrecondition)
}
size = nw.desc.Size
actual = nw.desc.Digest
@@ -650,42 +678,42 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64,
status, err := nw.w.Status()
if err != nil {
nw.w.Close()
return "", err
return "", 0, err
}
if size != 0 && size != status.Offset {
nw.w.Close()
return "", fmt.Errorf("%q failed size validation: %v != %v: %w", nw.ref, status.Offset, size, errdefs.ErrFailedPrecondition)
return "", 0, fmt.Errorf("%q failed size validation: %v != %v: %w", nw.ref, status.Offset, size, errdefs.ErrFailedPrecondition)
}
size = status.Offset
if err := nw.w.Commit(ctx, size, expected); err != nil && !errdefs.IsAlreadyExists(err) {
return "", err
return "", 0, err
}
actual = nw.w.Digest()
}
bkt, err := createBlobBucket(tx, nw.namespace, actual)
if err != nil {
if err == bolt.ErrBucketExists {
return actual, fmt.Errorf("content %v: %w", actual, errdefs.ErrAlreadyExists)
if err == errbolt.ErrBucketExists {
return actual, 0, fmt.Errorf("content %v: %w", actual, errdefs.ErrAlreadyExists)
}
return "", err
return "", 0, err
}
commitTime := time.Now().UTC()
sizeEncoded, err := encodeInt(size)
if err != nil {
return "", err
return "", 0, err
}
if err := boltutil.WriteTimestamps(bkt, commitTime, commitTime); err != nil {
return "", err
return "", 0, err
}
if err := boltutil.WriteLabels(bkt, base.Labels); err != nil {
return "", err
return "", 0, err
}
return actual, bkt.Put(bucketKeySize, sizeEncoded)
return actual, size, bkt.Put(bucketKeySize, sizeEncoded)
}
func (nw *namespacedWriter) Status() (st content.Status, err error) {

View File

@@ -95,7 +95,7 @@ type DB struct {
// a garbage collection to ensure the database is clean. This tracks
// the number of dirty operations. This should be updated and read
// atomically if outside of wlock.Lock.
dirty uint32
dirty atomic.Uint32
// dirtySS and dirtyCS flags keeps track of datastores which have had
// deletions since the last garbage collection. These datastores will
@@ -262,7 +262,7 @@ func (m *DB) Update(fn func(*bolt.Tx) error) error {
defer m.wlock.RUnlock()
err := m.db.Update(fn)
if err == nil {
dirty := atomic.LoadUint32(&m.dirty) > 0
dirty := m.dirty.Load() > 0
for _, fn := range m.mutationCallbacks {
fn(dirty)
}
@@ -431,8 +431,9 @@ func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) {
wg.Done()
}()
// reset dirty, no need for atomic inside of wlock.Lock
m.dirty = 0
// Reset dirty. Truly don't need to be atomically stored inside of the wlock
// but we're using the atomic wrappers that guarantee atomic access everywhere.
m.dirty.Store(0)
if len(m.dirtySS) > 0 {
var sl sync.Mutex

View File

@@ -171,7 +171,6 @@ func startGCContext(ctx context.Context, collectors map[gc.ResourceType]Collecto
if len(collectors) > 0 {
contexts = map[gc.ResourceType]CollectionContext{}
for rt, collector := range collectors {
rt := rt
c, err := collector.StartCollection(ctx)
if err != nil {
// Only skipping this resource this round

View File

@@ -22,7 +22,6 @@ import (
"errors"
"fmt"
"strings"
"sync/atomic"
"time"
eventstypes "github.com/containerd/containerd/api/events"
@@ -36,6 +35,7 @@ import (
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
bolt "go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
)
type imageStore struct {
@@ -143,7 +143,7 @@ func (s *imageStore) Create(ctx context.Context, image images.Image) (images.Ima
ibkt, err := bkt.CreateBucket([]byte(image.Name))
if err != nil {
if err != bolt.ErrBucketExists {
if err != errbolt.ErrBucketExists {
return err
}
@@ -319,13 +319,13 @@ func (s *imageStore) Delete(ctx context.Context, name string, opts ...images.Del
}
if err = bkt.DeleteBucket([]byte(name)); err != nil {
if err == bolt.ErrBucketNotFound {
if err == errbolt.ErrBucketNotFound {
err = fmt.Errorf("image %q: %w", name, errdefs.ErrNotFound)
}
return err
}
atomic.AddUint32(&s.db.dirty, 1)
s.db.dirty.Add(1)
return nil
})

View File

@@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"strings"
"sync/atomic"
"time"
"github.com/containerd/containerd/v2/core/leases"
@@ -31,6 +30,7 @@ import (
"github.com/containerd/errdefs"
digest "github.com/opencontainers/go-digest"
bolt "go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
)
// leaseManager manages the create/delete lifecycle of leases
@@ -72,7 +72,7 @@ func (lm *leaseManager) Create(ctx context.Context, opts ...leases.Opt) (leases.
txbkt, err := topbkt.CreateBucket([]byte(l.ID))
if err != nil {
if err == bolt.ErrBucketExists {
if err == errbolt.ErrBucketExists {
err = errdefs.ErrAlreadyExists
}
return fmt.Errorf("lease %q: %w", l.ID, err)
@@ -114,13 +114,13 @@ func (lm *leaseManager) Delete(ctx context.Context, lease leases.Lease, _ ...lea
return fmt.Errorf("lease %q: %w", lease.ID, errdefs.ErrNotFound)
}
if err := topbkt.DeleteBucket([]byte(lease.ID)); err != nil {
if err == bolt.ErrBucketNotFound {
if err == errbolt.ErrBucketNotFound {
err = fmt.Errorf("lease %q: %w", lease.ID, errdefs.ErrNotFound)
}
return err
}
atomic.AddUint32(&lm.db.dirty, 1)
lm.db.dirty.Add(1)
return nil
})
@@ -243,7 +243,7 @@ func (lm *leaseManager) DeleteResource(ctx context.Context, lease leases.Lease,
}
}
atomic.AddUint32(&lm.db.dirty, 1)
lm.db.dirty.Add(1)
return nil
})

View File

@@ -179,7 +179,7 @@ func migrateSandboxes(tx *bolt.Tx) error {
deletingBuckets := [][]byte{}
if merr := tx.ForEach(func(ns []byte, nsbkt *bolt.Bucket) error {
if err := tx.ForEach(func(ns []byte, nsbkt *bolt.Bucket) error {
// Skip v1 bucket, even if users created sandboxes in v1 namespace.
if bytes.Equal(bucketKeyVersion, ns) {
return nil
@@ -213,14 +213,14 @@ func migrateSandboxes(tx *bolt.Tx) error {
}
// copy single
if cerr := sbbkt.ForEach(func(key, value []byte) error {
if err := sbbkt.ForEach(func(key, value []byte) error {
if value == nil {
return nil
}
return tsbbkt.Put(key, value)
}); cerr != nil {
return cerr
}); err != nil {
return err
}
return sbbkt.ForEachBucket(func(subbkt []byte) error {
@@ -238,13 +238,13 @@ func migrateSandboxes(tx *bolt.Tx) error {
})
})
})
}); merr != nil {
}); err != nil {
return fmt.Errorf("failed to copy sandboxes into v1 bucket: %w", err)
}
for _, ns := range deletingBuckets {
derr := tx.DeleteBucket(ns)
if derr != nil {
err := tx.DeleteBucket(ns)
if err != nil {
return fmt.Errorf("failed to cleanup bucket %s in root: %w", ns, err)
}
}

View File

@@ -26,6 +26,7 @@ import (
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/errdefs"
bolt "go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
)
type namespaceStore struct {
@@ -56,7 +57,7 @@ func (s *namespaceStore) Create(ctx context.Context, namespace string, labels ma
// provides the already exists error.
bkt, err := topbkt.CreateBucket([]byte(namespace))
if err != nil {
if err == bolt.ErrBucketExists {
if err == errbolt.ErrBucketExists {
return fmt.Errorf("namespace %q: %w", namespace, errdefs.ErrAlreadyExists)
}
@@ -155,7 +156,7 @@ func (s *namespaceStore) Delete(ctx context.Context, namespace string, opts ...n
}
if err := bkt.DeleteBucket([]byte(namespace)); err != nil {
if err == bolt.ErrBucketNotFound {
if err == errbolt.ErrBucketNotFound {
return fmt.Errorf("namespace %q: %w", namespace, errdefs.ErrNotFound)
}

View File

@@ -32,6 +32,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/typeurl/v2"
"go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
)
const (
@@ -266,7 +267,7 @@ func (s *sandboxStore) Delete(ctx context.Context, id string) error {
}
if err := buckets.DeleteBucket([]byte(id)); err != nil {
if err == bbolt.ErrBucketNotFound {
if err == errbolt.ErrBucketNotFound {
err = errdefs.ErrNotFound
}
return fmt.Errorf("failed to delete sandbox %q: %w", id, err)
@@ -299,7 +300,7 @@ func (s *sandboxStore) write(parent *bbolt.Bucket, instance *api.Sandbox, overwr
} else {
bucket, err = parent.CreateBucket(id)
if err != nil {
if err == bbolt.ErrBucketExists {
if err == errbolt.ErrBucketExists {
return fmt.Errorf("sandbox bucket %q already exists: %w", instance.ID, errdefs.ErrAlreadyExists)
}
return err

View File

@@ -21,7 +21,6 @@ import (
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
eventstypes "github.com/containerd/containerd/api/events"
@@ -34,6 +33,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/log"
bolt "go.etcd.io/bbolt"
errbolt "go.etcd.io/bbolt/errors"
)
const (
@@ -456,7 +456,7 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re
bbkt, err := bkt.CreateBucket([]byte(key))
if err != nil {
if err != bolt.ErrBucketExists {
if err != errbolt.ErrBucketExists {
return err
}
if rerr == nil {
@@ -555,7 +555,7 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
}
bbkt, err := bkt.CreateBucket([]byte(name))
if err != nil {
if err == bolt.ErrBucketExists {
if err == errbolt.ErrBucketExists {
rerr = fmt.Errorf("snapshot %q: %w", name, errdefs.ErrAlreadyExists)
return nil
}
@@ -714,7 +714,7 @@ func (s *snapshotter) Remove(ctx context.Context, key string) error {
}
// Mark snapshotter as dirty for triggering garbage collection
atomic.AddUint32(&s.db.dirty, 1)
s.db.dirty.Add(1)
s.db.dirtySS[s.name] = struct{}{}
return nil

View File

@@ -26,49 +26,70 @@ import (
"golang.org/x/sys/unix"
)
// TODO: Support multiple mappings in future
func parseIDMapping(mapping string) ([]syscall.SysProcIDMap, error) {
func parseIDMapping(mapping string) (syscall.SysProcIDMap, error) {
var retval syscall.SysProcIDMap
parts := strings.Split(mapping, ":")
if len(parts) != 3 {
return nil, fmt.Errorf("user namespace mappings require the format `container-id:host-id:size`")
return retval, fmt.Errorf("user namespace mappings require the format `container-id:host-id:size`")
}
cID, err := strconv.Atoi(parts[0])
if err != nil {
return nil, fmt.Errorf("invalid container id for user namespace remapping, %w", err)
return retval, fmt.Errorf("invalid container id for user namespace remapping, %w", err)
}
hID, err := strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("invalid host id for user namespace remapping, %w", err)
return retval, fmt.Errorf("invalid host id for user namespace remapping, %w", err)
}
size, err := strconv.Atoi(parts[2])
if err != nil {
return nil, fmt.Errorf("invalid size for user namespace remapping, %w", err)
return retval, fmt.Errorf("invalid size for user namespace remapping, %w", err)
}
if cID < 0 || hID < 0 || size < 0 {
return nil, fmt.Errorf("invalid mapping %s, all IDs and size must be positive integers", mapping)
return retval, fmt.Errorf("invalid mapping %s, all IDs and size must be positive integers", mapping)
}
return []syscall.SysProcIDMap{
{
ContainerID: cID,
HostID: hID,
Size: size,
},
}, nil
retval = syscall.SysProcIDMap{
ContainerID: cID,
HostID: hID,
Size: size,
}
return retval, nil
}
// IDMapMount applies GID/UID shift according to gidmap/uidmap for target path
func parseIDMappingList(mappings string) ([]syscall.SysProcIDMap, error) {
var (
res []syscall.SysProcIDMap
maplist = strings.Split(mappings, ",")
)
for _, m := range maplist {
r, err := parseIDMapping(m)
if err != nil {
return nil, err
}
res = append(res, r)
}
return res, nil
}
// IDMapMount clones the mount at source to target, applying GID/UID idmapping of the user namespace for target path
func IDMapMount(source, target string, usernsFd int) (err error) {
return IDMapMountWithAttrs(source, target, usernsFd, 0, 0)
}
// IDMapMountWithAttrs clones the mount at source to target with the provided mount options and idmapping of the user namespace.
func IDMapMountWithAttrs(source, target string, usernsFd int, attrSet uint64, attrClr uint64) (err error) {
var (
attr unix.MountAttr
)
attr.Attr_set = unix.MOUNT_ATTR_IDMAP
attr.Attr_clr = 0
attr.Attr_set = unix.MOUNT_ATTR_IDMAP | attrSet
attr.Attr_clr = attrClr
attr.Propagation = 0
attr.Userns_fd = uint64(usernsFd)
@@ -79,7 +100,7 @@ func IDMapMount(source, target string, usernsFd int) (err error) {
defer unix.Close(dFd)
if err = unix.MountSetattr(dFd, "", unix.AT_EMPTY_PATH, &attr); err != nil {
return fmt.Errorf("Unable to shift GID/UID for %s: %w", target, err)
return fmt.Errorf("Unable to shift GID/UID or set mount attrs for %s: %w", target, err)
}
if err = unix.MoveMount(dFd, "", -int(unix.EBADF), target, unix.MOVE_MOUNT_F_EMPTY_PATH); err != nil {
@@ -88,15 +109,15 @@ func IDMapMount(source, target string, usernsFd int) (err error) {
return nil
}
// GetUsernsFD forks the current process and creates a user namespace using
// the specified mappings.
// GetUsernsFD forks the current process and creates a user namespace using the specified mappings.
// Expected syntax of ID mapping parameter is "%d:%d:%d[,%d:%d:%d,...]"
func GetUsernsFD(uidmap, gidmap string) (_usernsFD *os.File, _ error) {
uidMaps, err := parseIDMapping(uidmap)
uidMaps, err := parseIDMappingList(uidmap)
if err != nil {
return nil, err
}
gidMaps, err := parseIDMapping(gidmap)
gidMaps, err := parseIDMappingList(gidmap)
if err != nil {
return nil, err
}

View File

@@ -66,7 +66,12 @@ func prepareIDMappedOverlay(usernsFd int, options []string) ([]string, func(), e
return options, nil, fmt.Errorf("failed to parse overlay lowerdir's from given options")
}
tmpLowerdirs, idMapCleanUp, err := doPrepareIDMappedOverlay(lowerDirs, usernsFd)
tempRemountsLocation, err := os.MkdirTemp(tempMountLocation, "ovl-idmapped")
if err != nil {
return options, nil, fmt.Errorf("failed to create temporary overlay lowerdir mount location: %w", err)
}
tmpLowerdirs, idMapCleanUp, err := doPrepareIDMappedOverlay(tempRemountsLocation, lowerDirs, usernsFd)
if err != nil {
return options, idMapCleanUp, fmt.Errorf("failed to create idmapped mount: %w", err)
}
@@ -244,39 +249,35 @@ func getUnprivilegedMountFlags(path string) (int, error) {
return flags, nil
}
func doPrepareIDMappedOverlay(lowerDirs []string, usernsFd int) (tmpLowerDirs []string, _ func(), _ error) {
td, err := os.MkdirTemp(tempMountLocation, "ovl-idmapped")
if err != nil {
return nil, nil, err
}
func doPrepareIDMappedOverlay(tempRemountsLocation string, lowerDirs []string, usernsFd int) ([]string, func(), error) {
tmpLowerDirs := make([]string, 0, len(lowerDirs))
cleanUp := func() {
for _, lowerDir := range tmpLowerDirs {
// Do a detached unmount so even if the resource is busy, the mount will be
// gone (eventually) and we can safely delete the directory too.
if err := unix.Unmount(lowerDir, unix.MNT_DETACH); err != nil {
if err := unix.Unmount(lowerDir, 0); err != nil {
log.L.WithError(err).Warnf("failed to unmount temp lowerdir %s", lowerDir)
continue
}
// Using os.Remove() so if it's not empty, we don't delete files in the
// rootfs.
if err := os.Remove(lowerDir); err != nil {
log.L.WithError(err).Warnf("failed to remove temporary overlay lowerdir's")
log.L.WithError(err).Warnf("failed to remove temporary overlay lowerdir")
}
}
// This dir should be empty now. Otherwise, we don't do anything.
if err := os.Remove(filepath.Join(tmpLowerDirs[0], "..")); err != nil {
if err := os.Remove(tempRemountsLocation); err != nil {
log.L.WithError(err).Infof("failed to remove temporary overlay dir")
}
}
for i, lowerDir := range lowerDirs {
tmpLowerDir := filepath.Join(td, strconv.Itoa(i))
tmpLowerDir := filepath.Join(tempRemountsLocation, strconv.Itoa(i))
tmpLowerDirs = append(tmpLowerDirs, tmpLowerDir)
if err = os.MkdirAll(tmpLowerDir, 0700); err != nil {
if err := os.MkdirAll(tmpLowerDir, 0700); err != nil {
return nil, cleanUp, fmt.Errorf("failed to create temporary dir: %w", err)
}
if err = IDMapMount(lowerDir, tmpLowerDir, usernsFd); err != nil {
if err := IDMapMountWithAttrs(lowerDir, tmpLowerDir, usernsFd, unix.MOUNT_ATTR_RDONLY, 0); err != nil {
return nil, cleanUp, err
}
}

View File

@@ -1,4 +1,4 @@
//go:build !windows && !darwin
//go:build unix
/*
Copyright The containerd Authors.

View File

@@ -1,4 +1,4 @@
//go:build windows || darwin
//go:build windows
/*
Copyright The containerd Authors.

View File

@@ -78,10 +78,10 @@ type TokenOptions struct {
// FetchRefreshToken enables fetching a refresh token (aka "identity token", "offline token") along with the bearer token.
//
// For HTTP GET mode (FetchToken), FetchRefreshToken sets `offline_token=true` in the request.
// https://docs.docker.com/registry/spec/auth/token/#requesting-a-token
// https://distribution.github.io/distribution/spec/auth/token/#requesting-a-token
//
// For HTTP POST mode (FetchTokenWithOAuth), FetchRefreshToken sets `access_type=offline` in the request.
// https://docs.docker.com/registry/spec/auth/oauth/#getting-a-token
// https://distribution.github.io/distribution/spec/auth/oauth/#getting-a-token
FetchRefreshToken bool
}

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