vendor github.com/containers/image/v5@v5.2.0

See release notes:
	https://github.com/containers/image/releases/tag/v5.2.0

Fixes: #4877
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2020-02-03 16:09:04 +01:00
parent 5092c078ec
commit 801977b40d
38 changed files with 804 additions and 255 deletions

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/containernetworking/plugins v0.8.5 github.com/containernetworking/plugins v0.8.5
github.com/containers/buildah v1.13.1 github.com/containers/buildah v1.13.1
github.com/containers/conmon v2.0.10+incompatible github.com/containers/conmon v2.0.10+incompatible
github.com/containers/image/v5 v5.1.0 github.com/containers/image/v5 v5.2.0
github.com/containers/psgo v1.4.0 github.com/containers/psgo v1.4.0
github.com/containers/storage v1.15.8 github.com/containers/storage v1.15.8
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f

8
go.sum
View File

@ -30,6 +30,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
@ -91,6 +93,8 @@ github.com/containers/image/v5 v5.0.0 h1:arnXgbt1ucsC/ndtSpiQY87rA0UjhF+/xQnPzqd
github.com/containers/image/v5 v5.0.0/go.mod h1:MgiLzCfIeo8lrHi+4Lb8HP+rh513sm0Mlk6RrhjFOLY= github.com/containers/image/v5 v5.0.0/go.mod h1:MgiLzCfIeo8lrHi+4Lb8HP+rh513sm0Mlk6RrhjFOLY=
github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5dqTEo= github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5dqTEo=
github.com/containers/image/v5 v5.1.0/go.mod h1:BKlMD34WxRo1ruGHHEOrPQP0Qci7SWoPwU6fS7arsCU= github.com/containers/image/v5 v5.1.0/go.mod h1:BKlMD34WxRo1ruGHHEOrPQP0Qci7SWoPwU6fS7arsCU=
github.com/containers/image/v5 v5.2.0 h1:DowY5OII5x9Pb6Pt76vnHU79BgG4/jdwhZjeAj2R+t8=
github.com/containers/image/v5 v5.2.0/go.mod h1:IAub4gDGvXoxaIAdNy4e3FbVTDPVNMv9F0UfVVFbYCU=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM= github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM=
@ -370,6 +374,8 @@ github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6Sl
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4= github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4=
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA= github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA=
github.com/mtrmac/gpgme v0.1.1 h1:a5ISnvahzTzBH0m/klhehN68N+9+/jLwhpPFtH3oPAQ=
github.com/mtrmac/gpgme v0.1.1/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
@ -544,6 +550,8 @@ github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tL
github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/vbauerster/mpb/v4 v4.11.1 h1:ZOYQSVHgmeanXsbyC44aDg76tBGCS/54Rk8VkL8dJGA= github.com/vbauerster/mpb/v4 v4.11.1 h1:ZOYQSVHgmeanXsbyC44aDg76tBGCS/54Rk8VkL8dJGA=
github.com/vbauerster/mpb/v4 v4.11.1/go.mod h1:vMLa1J/ZKC83G2lB/52XpqT+ZZtFG4aZOdKhmpRL1uM= github.com/vbauerster/mpb/v4 v4.11.1/go.mod h1:vMLa1J/ZKC83G2lB/52XpqT+ZZtFG4aZOdKhmpRL1uM=
github.com/vbauerster/mpb/v4 v4.11.2 h1:ynkUoKzi65DZ1UsQPx7sgi/KN6G9f7br+Us2nKm35AM=
github.com/vbauerster/mpb/v4 v4.11.2/go.mod h1:jIuIRCltGJUnm6DCyPVkwjlLUk4nHTH+m4eD14CdFF0=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=

21
vendor/github.com/acarl005/stripansi/LICENSE generated vendored Normal file
View File

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

30
vendor/github.com/acarl005/stripansi/README.md generated vendored Normal file
View File

@ -0,0 +1,30 @@
Strip ANSI
==========
This Go package removes ANSI escape codes from strings.
Ideally, we would prevent these from appearing in any text we want to process.
However, sometimes this can't be helped, and we need to be able to deal with that noise.
This will use a regexp to remove those unwanted escape codes.
## Install
```sh
$ go get -u github.com/acarl005/stripansi
```
## Usage
```go
import (
"fmt"
"github.com/acarl005/stripansi"
)
func main() {
msg := "\x1b[38;5;140m foo\x1b[0m bar"
cleanMsg := stripansi.Strip(msg)
fmt.Println(cleanMsg) // " foo bar"
}
```

13
vendor/github.com/acarl005/stripansi/stripansi.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
package stripansi
import (
"regexp"
)
const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
var re = regexp.MustCompile(ansi)
func Strip(str string) string {
return re.ReplaceAllString(str, "")
}

View File

@ -709,7 +709,7 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst
wantedOS = sys.OSChoice wantedOS = sys.OSChoice
} }
if wantedOS != c.OS { if wantedOS != c.OS {
return fmt.Errorf("Image operating system mismatch: image uses %q, expecting %q", c.OS, wantedOS) logrus.Infof("Image operating system mismatch: image uses %q, expecting %q", c.OS, wantedOS)
} }
wantedArch := runtime.GOARCH wantedArch := runtime.GOARCH
@ -717,7 +717,7 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst
wantedArch = sys.ArchitectureChoice wantedArch = sys.ArchitectureChoice
} }
if wantedArch != c.Architecture { if wantedArch != c.Architecture {
return fmt.Errorf("Image architecture mismatch: image uses %q, expecting %q", c.Architecture, wantedArch) logrus.Infof("Image architecture mismatch: image uses %q, expecting %q", c.Architecture, wantedArch)
} }
} }
return nil return nil

View File

@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
@ -142,8 +143,11 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp
return types.BlobInfo{}, err return types.BlobInfo{}, err
} }
succeeded := false succeeded := false
explicitClosed := false
defer func() { defer func() {
blobFile.Close() if !explicitClosed {
blobFile.Close()
}
if !succeeded { if !succeeded {
os.Remove(blobFile.Name()) os.Remove(blobFile.Name())
} }
@ -164,10 +168,21 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp
if err := blobFile.Sync(); err != nil { if err := blobFile.Sync(); err != nil {
return types.BlobInfo{}, err return types.BlobInfo{}, err
} }
if err := blobFile.Chmod(0644); err != nil {
return types.BlobInfo{}, err // On POSIX systems, blobFile was created with mode 0600, so we need to make it readable.
// On Windows, the “permissions of newly created files” argument to syscall.Open is
// ignored and the file is already readable; besides, blobFile.Chmod, i.e. syscall.Fchmod,
// always fails on Windows.
if runtime.GOOS != "windows" {
if err := blobFile.Chmod(0644); err != nil {
return types.BlobInfo{}, err
}
} }
blobPath := d.ref.layerPath(computedDigest) blobPath := d.ref.layerPath(computedDigest)
// need to explicitly close the file, since a rename won't otherwise not work on Windows
blobFile.Close()
explicitClosed = true
if err := os.Rename(blobFile.Name(), blobPath); err != nil { if err := os.Rename(blobFile.Name(), blobPath); err != nil {
return types.BlobInfo{}, err return types.BlobInfo{}, err
} }

View File

@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -17,6 +16,7 @@ import (
"time" "time"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/pkg/docker/config" "github.com/containers/image/v5/pkg/docker/config"
"github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/image/v5/pkg/tlsclientconfig" "github.com/containers/image/v5/pkg/tlsclientconfig"
@ -45,6 +45,10 @@ const (
extensionSignatureSchemaVersion = 2 // extensionSignature.Version extensionSignatureSchemaVersion = 2 // extensionSignature.Version
extensionSignatureTypeAtomic = "atomic" // extensionSignature.Type extensionSignatureTypeAtomic = "atomic" // extensionSignature.Type
backoffNumIterations = 5
backoffInitialDelay = 2 * time.Second
backoffMaxDelay = 60 * time.Second
) )
var systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"} var systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"}
@ -277,7 +281,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password
} }
defer resp.Body.Close() defer resp.Body.Close()
return httpResponseToError(resp) return httpResponseToError(resp, "")
} }
// SearchResult holds the information of each matching image // SearchResult holds the information of each matching image
@ -351,7 +355,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
} else { } else {
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, httpResponseToError(resp)) logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, httpResponseToError(resp, ""))
} else { } else {
if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil { if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
return nil, err return nil, err
@ -368,7 +372,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
} else { } else {
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, httpResponseToError(resp)) logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, httpResponseToError(resp, ""))
} else { } else {
if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil { if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
return nil, err return nil, err
@ -400,74 +404,64 @@ func (c *dockerClient) makeRequest(ctx context.Context, method, path string, hea
return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope) return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope)
} }
// parseRetryAfter determines the delay required by the "Retry-After" header in res and returns it,
// silently falling back to fallbackDelay if the header is missing or invalid.
func parseRetryAfter(res *http.Response, fallbackDelay time.Duration) time.Duration {
after := res.Header.Get("Retry-After")
if after == "" {
return fallbackDelay
}
logrus.Debugf("Detected 'Retry-After' header %q", after)
// First, check if we have a numerical value.
if num, err := strconv.ParseInt(after, 10, 64); err == nil {
return time.Duration(num) * time.Second
}
// Second, check if we have an HTTP date.
// If the delta between the date and now is positive, use it.
// Otherwise, fall back to using the default exponential back off.
if t, err := http.ParseTime(after); err == nil {
delta := time.Until(t)
if delta > 0 {
return delta
}
logrus.Debugf("Retry-After date in the past, ignoring it")
return fallbackDelay
}
// If the header contents are bogus, fall back to using the default exponential back off.
logrus.Debugf("Invalid Retry-After format, ignoring it")
return fallbackDelay
}
// makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
// streamLen, if not -1, specifies the length of the data expected on stream. // streamLen, if not -1, specifies the length of the data expected on stream.
// makeRequest should generally be preferred. // makeRequest should generally be preferred.
// In case of an http 429 status code in the response, it performs an exponential back off starting at 2 seconds for at most 5 iterations. // In case of an HTTP 429 status code in the response, it may automatically retry a few times.
// If the `Retry-After` header is set in the response, the specified value or date is
// If the stream is non-nil, no back off will be performed.
// TODO(runcom): too many arguments here, use a struct // TODO(runcom): too many arguments here, use a struct
func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) {
var ( delay := backoffInitialDelay
res *http.Response attempts := 0
err error for {
delay int64 res, err := c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, extraScope)
) attempts++
delay = 2 if res == nil || res.StatusCode != http.StatusTooManyRequests || // Only retry on StatusTooManyRequests, success or other failure is returned to caller immediately
const numIterations = 5 stream != nil || // We can't retry with a body (which is not restartable in the general case)
const maxDelay = 60 attempts == backoffNumIterations {
return res, err
}
// math.Min() only supports float64, so have an anonymous func to avoid delay = parseRetryAfter(res, delay)
// casting. if delay > backoffMaxDelay {
min := func(a int64, b int64) int64 { delay = backoffMaxDelay
if a < b {
return a
} }
return b logrus.Debugf("Too many requests to %s: sleeping for %f seconds before next attempt", url, delay.Seconds())
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(delay):
// Nothing
}
delay = delay * 2 // exponential back off
} }
nextDelay := func(r *http.Response, delay int64) int64 {
after := res.Header.Get("Retry-After")
if after == "" {
return min(delay, maxDelay)
}
logrus.Debugf("detected 'Retry-After' header %q", after)
// First check if we have a numerical value.
if num, err := strconv.ParseInt(after, 10, 64); err == nil {
return min(num, maxDelay)
}
// Secondly check if we have an http date.
// If the delta between the date and now is positive, use it.
// Otherwise, fall back to using the default exponential back off.
if t, err := http.ParseTime(after); err == nil {
delta := int64(time.Until(t).Seconds())
if delta > 0 {
return min(delta, maxDelay)
}
logrus.Debugf("negative date: falling back to using %d seconds", delay)
return min(delay, maxDelay)
}
// If the header contains bogus, fall back to using the default
// exponential back off.
logrus.Debugf("invalid format: falling back to using %d seconds", delay)
return min(delay, maxDelay)
}
for i := 0; i < numIterations; i++ {
res, err = c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, extraScope)
if stream == nil && res != nil && res.StatusCode == http.StatusTooManyRequests {
if i < numIterations-1 {
logrus.Errorf("HEADER %v", res.Header)
delay = nextDelay(res, delay) // compute next delay - does NOT exceed maxDelay
logrus.Debugf("too many request to %s: sleeping for %d seconds before next attempt", url, delay)
time.Sleep(time.Duration(delay) * time.Second)
delay = delay * 2 // exponential back off
}
continue
}
break
}
return res, err
} }
// makeRequestToResolvedURLOnce creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // makeRequestToResolvedURLOnce creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
@ -597,7 +591,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
default: default:
return nil, errors.Errorf("unexpected http code: %d (%s), URL: %s", res.StatusCode, http.StatusText(res.StatusCode), authReq.URL) return nil, errors.Errorf("unexpected http code: %d (%s), URL: %s", res.StatusCode, http.StatusText(res.StatusCode), authReq.URL)
} }
tokenBlob, err := ioutil.ReadAll(res.Body) tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -627,7 +621,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error {
defer resp.Body.Close() defer resp.Body.Close()
logrus.Debugf("Ping %s status %d", url, resp.StatusCode) logrus.Debugf("Ping %s status %d", url, resp.StatusCode)
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized { if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
return httpResponseToError(resp) return httpResponseToError(resp, "")
} }
c.challenges = parseAuthHeader(resp.Header) c.challenges = parseAuthHeader(resp.Header)
c.scheme = scheme c.scheme = scheme
@ -690,7 +684,7 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe
return nil, errors.Wrapf(clientLib.HandleErrorResponse(res), "Error downloading signatures for %s in %s", manifestDigest, ref.ref.Name()) return nil, errors.Wrapf(clientLib.HandleErrorResponse(res), "Error downloading signatures for %s in %s", manifestDigest, ref.ref.Name())
} }
body, err := ioutil.ReadAll(res.Body) body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureListBodySize)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -70,7 +70,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
return nil, err return nil, err
} }
defer res.Body.Close() defer res.Body.Close()
if err := httpResponseToError(res); err != nil { if err := httpResponseToError(res, "Error fetching tags list"); err != nil {
return nil, err return nil, err
} }

View File

@ -15,6 +15,7 @@ import (
"strings" "strings"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/pkg/blobinfocache/none"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
@ -58,14 +59,16 @@ func (d *dockerImageDestination) Close() error {
} }
func (d *dockerImageDestination) SupportedManifestMIMETypes() []string { func (d *dockerImageDestination) SupportedManifestMIMETypes() []string {
return []string{ mimeTypes := []string{
imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeImageManifest,
manifest.DockerV2Schema2MediaType, manifest.DockerV2Schema2MediaType,
imgspecv1.MediaTypeImageIndex, imgspecv1.MediaTypeImageIndex,
manifest.DockerV2ListMediaType, manifest.DockerV2ListMediaType,
manifest.DockerV2Schema1SignedMediaType,
manifest.DockerV2Schema1MediaType,
} }
if d.c.sys == nil || !d.c.sys.DockerDisableDestSchema1MIMETypes {
mimeTypes = append(mimeTypes, manifest.DockerV2Schema1SignedMediaType, manifest.DockerV2Schema1MediaType)
}
return mimeTypes
} }
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures. // SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
@ -620,7 +623,7 @@ sigExists:
} }
defer res.Body.Close() defer res.Body.Close()
if res.StatusCode != http.StatusCreated { if res.StatusCode != http.StatusCreated {
body, err := ioutil.ReadAll(res.Body) body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxErrorBodySize)
if err == nil { if err == nil {
logrus.Debugf("Error body %s", string(body)) logrus.Debugf("Error body %s", string(body))
} }

View File

@ -10,8 +10,10 @@ import (
"net/url" "net/url"
"os" "os"
"strconv" "strconv"
"strings"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
@ -53,43 +55,77 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef
// non-mirror original location last; this both transparently handles the case // non-mirror original location last; this both transparently handles the case
// of no mirrors configured, and ensures we return the error encountered when // of no mirrors configured, and ensures we return the error encountered when
// acessing the upstream location if all endpoints fail. // acessing the upstream location if all endpoints fail.
manifestLoadErr := errors.New("Internal error: newImageSource returned without trying any endpoint")
pullSources, err := registry.PullSourcesFromReference(ref.ref) pullSources, err := registry.PullSourcesFromReference(ref.ref)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, pullSource := range pullSources { type attempt struct {
logrus.Debugf("Trying to pull %q", pullSource.Reference) ref reference.Named
dockerRef, err := newReference(pullSource.Reference) err error
if err != nil {
return nil, err
}
endpointSys := sys
// sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors.
if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(dockerRef.ref) != primaryDomain {
copy := *endpointSys
copy.DockerAuthConfig = nil
endpointSys = &copy
}
client, err := newDockerClientFromRef(endpointSys, dockerRef, false, "pull")
if err != nil {
return nil, err
}
client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure
testImageSource := &dockerImageSource{
ref: dockerRef,
c: client,
}
manifestLoadErr = testImageSource.ensureManifestIsLoaded(ctx)
if manifestLoadErr == nil {
return testImageSource, nil
}
} }
return nil, manifestLoadErr attempts := []attempt{}
for _, pullSource := range pullSources {
logrus.Debugf("Trying to access %q", pullSource.Reference)
s, err := newImageSourceAttempt(ctx, sys, pullSource, primaryDomain)
if err == nil {
return s, nil
}
logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, err)
attempts = append(attempts, attempt{
ref: pullSource.Reference,
err: err,
})
}
switch len(attempts) {
case 0:
return nil, errors.New("Internal error: newImageSource returned without trying any endpoint")
case 1:
return nil, attempts[0].err // If no mirrors are used, perfectly preserve the error type and add no noise.
default:
// Dont just build a string, try to preserve the typed error.
primary := &attempts[len(attempts)-1]
extras := []string{}
for i := 0; i < len(attempts)-1; i++ {
// This is difficult to fit into a single-line string, when the error can contain arbitrary strings including any metacharacters we decide to use.
// The paired [] at least have some chance of being unambiguous.
extras = append(extras, fmt.Sprintf("[%s: %v]", attempts[i].ref.String(), attempts[i].err))
}
return nil, errors.Wrapf(primary.err, "(Mirrors also failed: %s): %s", strings.Join(extras, "\n"), primary.ref.String())
}
}
// newImageSourceAttempt is an internal helper for newImageSource. Everyone else must call newImageSource.
// Given a pullSource and primaryDomain, return a dockerImageSource if it is reachable.
// The caller must call .Close() on the returned ImageSource.
func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, pullSource sysregistriesv2.PullSource, primaryDomain string) (*dockerImageSource, error) {
ref, err := newReference(pullSource.Reference)
if err != nil {
return nil, err
}
endpointSys := sys
// sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors.
if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(ref.ref) != primaryDomain {
copy := *endpointSys
copy.DockerAuthConfig = nil
endpointSys = &copy
}
client, err := newDockerClientFromRef(endpointSys, ref, false, "pull")
if err != nil {
return nil, err
}
client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure
s := &dockerImageSource{
ref: ref,
c: client,
}
if err := s.ensureManifestIsLoaded(ctx); err != nil {
return nil, err
}
return s, nil
} }
// Reference returns the reference used to set up this source, _as specified by the user_ // Reference returns the reference used to set up this source, _as specified by the user_
@ -156,7 +192,8 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.ref.ref.Name()) return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.ref.ref.Name())
} }
manblob, err := ioutil.ReadAll(res.Body)
manblob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxManifestBodySize)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
@ -239,7 +276,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
if err := httpResponseToError(res); err != nil { if err := httpResponseToError(res, "Error fetching blob"); err != nil {
return nil, 0, err return nil, 0, err
} }
cache.RecordKnownLocation(s.ref.Transport(), bicTransportScope(s.ref), info.Digest, newBICLocationReference(s.ref)) cache.RecordKnownLocation(s.ref.Transport(), bicTransportScope(s.ref), info.Digest, newBICLocationReference(s.ref))
@ -342,7 +379,7 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) (
} else if res.StatusCode != http.StatusOK { } else if res.StatusCode != http.StatusOK {
return nil, false, errors.Errorf("Error reading signature from %s: status %d (%s)", url.String(), res.StatusCode, http.StatusText(res.StatusCode)) return nil, false, errors.Errorf("Error reading signature from %s: status %d (%s)", url.String(), res.StatusCode, http.StatusText(res.StatusCode))
} }
sig, err := ioutil.ReadAll(res.Body) sig, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureBodySize)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
@ -401,7 +438,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
return err return err
} }
defer get.Body.Close() defer get.Body.Close()
manifestBody, err := ioutil.ReadAll(get.Body) manifestBody, err := iolimits.ReadAtMost(get.Body, iolimits.MaxManifestBodySize)
if err != nil { if err != nil {
return err return err
} }
@ -424,7 +461,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
} }
defer delete.Body.Close() defer delete.Body.Close()
body, err := ioutil.ReadAll(delete.Body) body, err := iolimits.ReadAtMost(delete.Body, iolimits.MaxErrorBodySize)
if err != nil { if err != nil {
return err return err
} }

View File

@ -14,7 +14,7 @@ var (
// docker V1 registry. // docker V1 registry.
ErrV1NotSupported = errors.New("can't talk to a V1 docker registry") ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
// ErrTooManyRequests is returned when the status code returned is 429 // ErrTooManyRequests is returned when the status code returned is 429
ErrTooManyRequests = errors.New("too many request to registry") ErrTooManyRequests = errors.New("too many requests to registry")
) )
// ErrUnauthorizedForCredentials is returned when the status code returned is 401 // ErrUnauthorizedForCredentials is returned when the status code returned is 401
@ -26,9 +26,9 @@ func (e ErrUnauthorizedForCredentials) Error() string {
return fmt.Sprintf("unable to retrieve auth token: invalid username/password: %s", e.Err.Error()) return fmt.Sprintf("unable to retrieve auth token: invalid username/password: %s", e.Err.Error())
} }
// httpResponseToError translates the https.Response into an error. It returns // httpResponseToError translates the https.Response into an error, possibly prefixing it with the supplied context. It returns
// nil if the response is not considered an error. // nil if the response is not considered an error.
func httpResponseToError(res *http.Response) error { func httpResponseToError(res *http.Response, context string) error {
switch res.StatusCode { switch res.StatusCode {
case http.StatusOK: case http.StatusOK:
return nil return nil
@ -38,6 +38,9 @@ func httpResponseToError(res *http.Response) error {
err := client.HandleErrorResponse(res) err := client.HandleErrorResponse(res)
return ErrUnauthorizedForCredentials{Err: err} return ErrUnauthorizedForCredentials{Err: err}
default: default:
return perrors.Errorf("invalid status code from registry %d (%s)", res.StatusCode, http.StatusText(res.StatusCode)) if context != "" {
context = context + ": "
}
return perrors.Errorf("%sinvalid status code from registry %d (%s)", context, res.StatusCode, http.StatusText(res.StatusCode))
} }
} }

View File

@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/internal/tmpdir" "github.com/containers/image/v5/internal/tmpdir"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
@ -143,7 +144,7 @@ func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo t
} }
if isConfig { if isConfig {
buf, err := ioutil.ReadAll(stream) buf, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize)
if err != nil { if err != nil {
return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream") return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream")
} }

View File

@ -11,6 +11,7 @@ import (
"path" "path"
"sync" "sync"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/internal/tmpdir" "github.com/containers/image/v5/internal/tmpdir"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/pkg/compression"
@ -203,13 +204,13 @@ func findTarComponent(inputFile io.Reader, path string) (*tar.Reader, *tar.Heade
} }
// readTarComponent returns full contents of componentPath. // readTarComponent returns full contents of componentPath.
func (s *Source) readTarComponent(path string) ([]byte, error) { func (s *Source) readTarComponent(path string, limit int) ([]byte, error) {
file, err := s.openTarComponent(path) file, err := s.openTarComponent(path)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "Error loading tar component %s", path) return nil, errors.Wrapf(err, "Error loading tar component %s", path)
} }
defer file.Close() defer file.Close()
bytes, err := ioutil.ReadAll(file) bytes, err := iolimits.ReadAtMost(file, limit)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -240,7 +241,7 @@ func (s *Source) ensureCachedDataIsPresentPrivate() error {
} }
// Read and parse config. // Read and parse config.
configBytes, err := s.readTarComponent(tarManifest[0].Config) configBytes, err := s.readTarComponent(tarManifest[0].Config, iolimits.MaxConfigBodySize)
if err != nil { if err != nil {
return err return err
} }
@ -266,7 +267,7 @@ func (s *Source) ensureCachedDataIsPresentPrivate() error {
// loadTarManifest loads and decodes the manifest.json. // loadTarManifest loads and decodes the manifest.json.
func (s *Source) loadTarManifest() ([]ManifestItem, error) { func (s *Source) loadTarManifest() ([]ManifestItem, error) {
// FIXME? Do we need to deal with the legacy format? // FIXME? Do we need to deal with the legacy format?
bytes, err := s.readTarComponent(manifestFileName) bytes, err := s.readTarComponent(manifestFileName, iolimits.MaxTarFileManifestSize)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -7,10 +7,10 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"strings" "strings"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/pkg/blobinfocache/none"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
@ -102,7 +102,7 @@ func (m *manifestSchema2) ConfigBlob(ctx context.Context) ([]byte, error) {
return nil, err return nil, err
} }
defer stream.Close() defer stream.Close()
blob, err := ioutil.ReadAll(stream) blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -4,9 +4,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/pkg/blobinfocache/none"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
@ -67,7 +67,7 @@ func (m *manifestOCI1) ConfigBlob(ctx context.Context) ([]byte, error) {
return nil, err return nil, err
} }
defer stream.Close() defer stream.Close()
blob, err := ioutil.ReadAll(stream) blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -0,0 +1,60 @@
package iolimits
import (
"io"
"io/ioutil"
"github.com/pkg/errors"
)
// All constants below are intended to be used as limits for `ReadAtMost`. The
// immediate use-case for limiting the size of in-memory copied data is to
// protect against OOM DOS attacks as described inCVE-2020-1702. Instead of
// copying data until running out of memory, we error out after hitting the
// specified limit.
const (
// megaByte denotes one megabyte and is intended to be used as a limit in
// `ReadAtMost`.
megaByte = 1 << 20
// MaxManifestBodySize is the maximum allowed size of a manifest. The limit
// of 4 MB aligns with the one of a Docker registry:
// https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/handlers/manifests.go#L30
MaxManifestBodySize = 4 * megaByte
// MaxAuthTokenBodySize is the maximum allowed size of an auth token.
// The limit of 1 MB is considered to be greatly sufficient.
MaxAuthTokenBodySize = megaByte
// MaxSignatureListBodySize is the maximum allowed size of a signature list.
// The limit of 4 MB is considered to be greatly sufficient.
MaxSignatureListBodySize = 4 * megaByte
// MaxSignatureBodySize is the maximum allowed size of a signature.
// The limit of 4 MB is considered to be greatly sufficient.
MaxSignatureBodySize = 4 * megaByte
// MaxErrorBodySize is the maximum allowed size of an error-response body.
// The limit of 1 MB is considered to be greatly sufficient.
MaxErrorBodySize = megaByte
// MaxConfigBodySize is the maximum allowed size of a config blob.
// The limit of 4 MB is considered to be greatly sufficient.
MaxConfigBodySize = 4 * megaByte
// MaxOpenShiftStatusBody is the maximum allowed size of an OpenShift status body.
// The limit of 4 MB is considered to be greatly sufficient.
MaxOpenShiftStatusBody = 4 * megaByte
// MaxTarFileManifestSize is the maximum allowed size of a (docker save)-like manifest (which may contain multiple images)
// The limit of 1 MB is considered to be greatly sufficient.
MaxTarFileManifestSize = megaByte
)
// ReadAtMost reads from reader and errors out if the specified limit (in bytes) is exceeded.
func ReadAtMost(reader io.Reader, limit int) ([]byte, error) {
limitedReader := io.LimitReader(reader, int64(limit+1))
res, err := ioutil.ReadAll(limitedReader)
if err != nil {
return nil, err
}
if len(res) > limit {
return nil, errors.Errorf("exceeded maximum allowed size of %d bytes", limit)
}
return res, nil
}

View File

@ -7,13 +7,13 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"github.com/containers/image/v5/docker" "github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
"github.com/containers/image/v5/version" "github.com/containers/image/v5/version"
@ -102,7 +102,7 @@ func (c *openshiftClient) doRequest(ctx context.Context, method, path string, re
return nil, err return nil, err
} }
defer res.Body.Close() defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body) body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxOpenShiftStatusBody)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -139,7 +139,7 @@ func (m *gpgmeSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte,
} }
// Verify parses unverifiedSignature and returns the content and the signer's identity // Verify parses unverifiedSignature and returns the content and the signer's identity
func (m gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) { func (m *gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
signedBuffer := bytes.Buffer{} signedBuffer := bytes.Buffer{}
signedData, err := gpgme.NewDataWriter(&signedBuffer) signedData, err := gpgme.NewDataWriter(&signedBuffer)
if err != nil { if err != nil {
@ -170,6 +170,6 @@ func (m gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []by
// WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) // WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys)
// is NOT the same as a "key identity" used in other calls ot this interface, and // is NOT the same as a "key identity" used in other calls ot this interface, and
// the values may have no recognizable relationship if the public key is not available. // the values may have no recognizable relationship if the public key is not available.
func (m gpgmeSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { func (m *gpgmeSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
return gpgUntrustedSignatureContents(untrustedSignature) return gpgUntrustedSignatureContents(untrustedSignature)
} }

View File

@ -154,6 +154,6 @@ func (m *openpgpSigningMechanism) Verify(unverifiedSignature []byte) (contents [
// WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) // WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys)
// is NOT the same as a "key identity" used in other calls ot this interface, and // is NOT the same as a "key identity" used in other calls ot this interface, and
// the values may have no recognizable relationship if the public key is not available. // the values may have no recognizable relationship if the public key is not available.
func (m openpgpSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { func (m *openpgpSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
return gpgUntrustedSignatureContents(untrustedSignature) return gpgUntrustedSignatureContents(untrustedSignature)
} }

View File

@ -547,6 +547,8 @@ type SystemContext struct {
// Note that this field is used mainly to integrate containers/image into projectatomic/docker // Note that this field is used mainly to integrate containers/image into projectatomic/docker
// in order to not break any existing docker's integration tests. // in order to not break any existing docker's integration tests.
DockerDisableV1Ping bool DockerDisableV1Ping bool
// If true, dockerImageDestination.SupportedManifestMIMETypes will omit the Schema1 media types from the supported list
DockerDisableDestSchema1MIMETypes bool
// Directory to use for OSTree temporary files // Directory to use for OSTree temporary files
OSTreeTmpDirPath string OSTreeTmpDirPath string

View File

@ -6,7 +6,7 @@ const (
// VersionMajor is for an API incompatible changes // VersionMajor is for an API incompatible changes
VersionMajor = 5 VersionMajor = 5
// VersionMinor is for functionality in a backwards-compatible manner // VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 1 VersionMinor = 2
// VersionPatch is for backwards-compatible bug fixes // VersionPatch is for backwards-compatible bug fixes
VersionPatch = 0 VersionPatch = 0

40
vendor/github.com/mtrmac/gpgme/.appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,40 @@
---
version: 0.{build}
platform: x86
branches:
only:
- master
clone_folder: c:\gopath\src\github.com\proglottis\gpgme
environment:
GOPATH: c:\gopath
GOROOT: C:\go-x86
CGO_LDFLAGS: -LC:\gpg\lib
CGO_CFLAGS: -IC:\gpg\include
GPG_DIR: C:\gpg
install:
- nuget install 7ZipCLI -ExcludeVersion
- set PATH=%appveyor_build_folder%\7ZipCLI\tools;%PATH%
- appveyor DownloadFile https://www.gnupg.org/ftp/gcrypt/binary/gnupg-w32-2.1.20_20170403.exe -FileName gnupg-w32-2.1.20_20170403.exe
- 7z x -o%GPG_DIR% gnupg-w32-2.1.20_20170403.exe
- copy "%GPG_DIR%\lib\libgpg-error.imp" "%GPG_DIR%\lib\libgpg-error.a"
- copy "%GPG_DIR%\lib\libassuan.imp" "%GPG_DIR%\lib\libassuan.a"
- copy "%GPG_DIR%\lib\libgpgme.imp" "%GPG_DIR%\lib\libgpgme.a"
- set PATH=%GOPATH%\bin;%GOROOT%\bin;%GPG_DIR%\bin;C:\MinGW\bin;%PATH%
- C:\cygwin\bin\sed -i 's/"GPG_AGENT_INFO"/"GPG_AGENT_INFO="/;s/C.unsetenv(v)/C.putenv(v)/' %APPVEYOR_BUILD_FOLDER%\gpgme.go
test_script:
- go test -v github.com/proglottis/gpgme
build_script:
- go build -o example_decrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\decrypt.go
- go build -o example_encrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\encrypt.go
artifacts:
- path: example_decrypt.exe
name: decrypt example binary
- path: example_encrypt.exe
name: encrypt example binary

32
vendor/github.com/mtrmac/gpgme/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,32 @@
---
language: go
os:
- linux
- osx
- windows
dist: xenial
sudo: false
go:
- 1.11
- 1.12
- 1.13
addons:
apt:
packages:
- libgpgme11-dev
homebrew:
packages:
- gnupg
- gnupg@1.4
- gpgme
update: true
matrix:
allow_failures:
- os: windows
before_install:
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install msys2; fi
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install gpg4win; fi

View File

@ -50,25 +50,25 @@ func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t {
} }
//export gogpgme_seekfunc //export gogpgme_seekfunc
func gogpgme_seekfunc(handle unsafe.Pointer, offset C.off_t, whence C.int) C.off_t { func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) C.gpgme_off_t {
d := callbackLookup(uintptr(handle)).(*Data) d := callbackLookup(uintptr(handle)).(*Data)
n, err := d.s.Seek(int64(offset), int(whence)) n, err := d.s.Seek(int64(offset), int(whence))
if err != nil { if err != nil {
C.gpgme_err_set_errno(C.EIO) C.gpgme_err_set_errno(C.EIO)
return -1 return -1
} }
return C.off_t(n) return C.gpgme_off_t(n)
} }
// The Data buffer used to communicate with GPGME // The Data buffer used to communicate with GPGME
type Data struct { type Data struct {
dh C.gpgme_data_t dh C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C
buf []byte buf []byte
cbs C.struct_gpgme_data_cbs cbs C.struct_gpgme_data_cbs
r io.Reader r io.Reader
w io.Writer w io.Writer
s io.Seeker s io.Seeker
cbc uintptr cbc uintptr // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh)
} }
func newData() *Data { func newData() *Data {
@ -154,12 +154,14 @@ func (d *Data) Close() error {
callbackDelete(d.cbc) callbackDelete(d.cbc)
} }
_, err := C.gpgme_data_release(d.dh) _, err := C.gpgme_data_release(d.dh)
runtime.KeepAlive(d)
d.dh = nil d.dh = nil
return err return err
} }
func (d *Data) Write(p []byte) (int, error) { func (d *Data) Write(p []byte) (int, error) {
n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
runtime.KeepAlive(d)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -171,6 +173,7 @@ func (d *Data) Write(p []byte) (int, error) {
func (d *Data) Read(p []byte) (int, error) { func (d *Data) Read(p []byte) (int, error) {
n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
runtime.KeepAlive(d)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -181,11 +184,14 @@ func (d *Data) Read(p []byte) (int, error) {
} }
func (d *Data) Seek(offset int64, whence int) (int64, error) { func (d *Data) Seek(offset int64, whence int) (int64, error) {
n, err := C.gpgme_data_seek(d.dh, C.off_t(offset), C.int(whence)) n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence))
runtime.KeepAlive(d)
return int64(n), err return int64(n), err
} }
// Name returns the associated filename if any // Name returns the associated filename if any
func (d *Data) Name() string { func (d *Data) Name() string {
return C.GoString(C.gpgme_data_get_file_name(d.dh)) res := C.GoString(C.gpgme_data_get_file_name(d.dh))
runtime.KeepAlive(d)
return res
} }

3
vendor/github.com/mtrmac/gpgme/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/mtrmac/gpgme
go 1.11

View File

@ -8,6 +8,28 @@ void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintpt
gpgme_set_passphrase_cb(ctx, cb, (void *)handle); gpgme_set_passphrase_cb(ctx, cb, (void *)handle);
} }
gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence) {
return gpgme_data_seek(dh, offset, whence);
}
gpgme_error_t gogpgme_op_assuan_transact_ext(
gpgme_ctx_t ctx,
char* cmd,
uintptr_t data_h,
uintptr_t inquiry_h,
uintptr_t status_h,
gpgme_error_t *operr
){
return gpgme_op_assuan_transact_ext(
ctx,
cmd,
(gpgme_assuan_data_cb_t) gogpgme_assuan_data_callback, (void *)data_h,
(gpgme_assuan_inquire_cb_t) gogpgme_assuan_inquiry_callback, (void *)inquiry_h,
(gpgme_assuan_status_cb_t) gogpgme_assuan_status_callback, (void *)status_h,
operr
);
}
unsigned int key_revoked(gpgme_key_t k) { unsigned int key_revoked(gpgme_key_t k) {
return k->revoked; return k->revoked;
} }

View File

@ -12,6 +12,13 @@ extern off_t gogpgme_seekfunc(void *handle, off_t offset, int whence);
extern gpgme_error_t gogpgme_passfunc(void *hook, char *uid_hint, char *passphrase_info, int prev_was_bad, int fd); extern gpgme_error_t gogpgme_passfunc(void *hook, char *uid_hint, char *passphrase_info, int prev_was_bad, int fd);
extern gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle); extern gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle);
extern void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle); extern void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle);
extern gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence);
extern gpgme_error_t gogpgme_op_assuan_transact_ext(gpgme_ctx_t ctx, char *cmd, uintptr_t data_h, uintptr_t inquiry_h , uintptr_t status_h, gpgme_error_t *operr);
extern gpgme_error_t gogpgme_assuan_data_callback(void *opaque, void* data, size_t datalen );
extern gpgme_error_t gogpgme_assuan_inquiry_callback(void *opaque, char* name, char* args);
extern gpgme_error_t gogpgme_assuan_status_callback(void *opaque, char* status, char* args);
extern unsigned int key_revoked(gpgme_key_t k); extern unsigned int key_revoked(gpgme_key_t k);
extern unsigned int key_expired(gpgme_key_t k); extern unsigned int key_expired(gpgme_key_t k);

View File

@ -7,7 +7,6 @@ package gpgme
// #include <gpgme.h> // #include <gpgme.h>
// #include "go_gpgme.h" // #include "go_gpgme.h"
import "C" import "C"
import ( import (
"fmt" "fmt"
"io" "io"
@ -48,9 +47,8 @@ const (
ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN
ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13 ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13
ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER
// ProtocolSpawn Protocol = C.GPGME_PROTOCOL_SPAWN // Unavailable in 1.4.3 ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT
ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN
ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN
) )
type PinEntryMode int type PinEntryMode int
@ -70,7 +68,6 @@ const (
EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO
EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE
EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN
// EncryptNoCompress EncryptFlag = C.GPGME_ENCRYPT_NO_COMPRESS // Unavailable in 1.4.3
) )
type HashAlgo int type HashAlgo int
@ -84,7 +81,6 @@ const (
KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN
KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS
KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS
// KeyListModeWithSecret KeyListMode = C.GPGME_KEYLIST_MODE_WITH_SECRET // Unavailable in 1.4.3
KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL
KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE
) )
@ -168,39 +164,60 @@ func EngineCheckVersion(p Protocol) error {
} }
type EngineInfo struct { type EngineInfo struct {
info C.gpgme_engine_info_t next *EngineInfo
protocol Protocol
fileName string
homeDir string
version string
requiredVersion string
}
func copyEngineInfo(info C.gpgme_engine_info_t) *EngineInfo {
res := &EngineInfo{
next: nil,
protocol: Protocol(info.protocol),
fileName: C.GoString(info.file_name),
homeDir: C.GoString(info.home_dir),
version: C.GoString(info.version),
requiredVersion: C.GoString(info.req_version),
}
if info.next != nil {
res.next = copyEngineInfo(info.next)
}
return res
} }
func (e *EngineInfo) Next() *EngineInfo { func (e *EngineInfo) Next() *EngineInfo {
if e.info.next == nil { return e.next
return nil
}
return &EngineInfo{info: e.info.next}
} }
func (e *EngineInfo) Protocol() Protocol { func (e *EngineInfo) Protocol() Protocol {
return Protocol(e.info.protocol) return e.protocol
} }
func (e *EngineInfo) FileName() string { func (e *EngineInfo) FileName() string {
return C.GoString(e.info.file_name) return e.fileName
} }
func (e *EngineInfo) Version() string { func (e *EngineInfo) Version() string {
return C.GoString(e.info.version) return e.version
} }
func (e *EngineInfo) RequiredVersion() string { func (e *EngineInfo) RequiredVersion() string {
return C.GoString(e.info.req_version) return e.requiredVersion
} }
func (e *EngineInfo) HomeDir() string { func (e *EngineInfo) HomeDir() string {
return C.GoString(e.info.home_dir) return e.homeDir
} }
func GetEngineInfo() (*EngineInfo, error) { func GetEngineInfo() (*EngineInfo, error) {
info := &EngineInfo{} var cInfo C.gpgme_engine_info_t
return info, handleError(C.gpgme_get_engine_info(&info.info)) err := handleError(C.gpgme_get_engine_info(&cInfo))
if err != nil {
return nil, err
}
return copyEngineInfo(cInfo), nil // It is up to the caller not to invalidate cInfo concurrently until this is done.
} }
func SetEngineInfo(proto Protocol, fileName, homeDir string) error { func SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@ -261,9 +278,9 @@ type Context struct {
KeyError error KeyError error
callback Callback callback Callback
cbc uintptr cbc uintptr // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx)
ctx C.gpgme_ctx_t ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C
} }
func New() (*Context, error) { func New() (*Context, error) {
@ -281,49 +298,68 @@ func (c *Context) Release() {
callbackDelete(c.cbc) callbackDelete(c.cbc)
} }
C.gpgme_release(c.ctx) C.gpgme_release(c.ctx)
runtime.KeepAlive(c)
c.ctx = nil c.ctx = nil
} }
func (c *Context) SetArmor(yes bool) { func (c *Context) SetArmor(yes bool) {
C.gpgme_set_armor(c.ctx, cbool(yes)) C.gpgme_set_armor(c.ctx, cbool(yes))
runtime.KeepAlive(c)
} }
func (c *Context) Armor() bool { func (c *Context) Armor() bool {
return C.gpgme_get_armor(c.ctx) != 0 res := C.gpgme_get_armor(c.ctx) != 0
runtime.KeepAlive(c)
return res
} }
func (c *Context) SetTextMode(yes bool) { func (c *Context) SetTextMode(yes bool) {
C.gpgme_set_textmode(c.ctx, cbool(yes)) C.gpgme_set_textmode(c.ctx, cbool(yes))
runtime.KeepAlive(c)
} }
func (c *Context) TextMode() bool { func (c *Context) TextMode() bool {
return C.gpgme_get_textmode(c.ctx) != 0 res := C.gpgme_get_textmode(c.ctx) != 0
runtime.KeepAlive(c)
return res
} }
func (c *Context) SetProtocol(p Protocol) error { func (c *Context) SetProtocol(p Protocol) error {
return handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p)))
runtime.KeepAlive(c)
return err
} }
func (c *Context) Protocol() Protocol { func (c *Context) Protocol() Protocol {
return Protocol(C.gpgme_get_protocol(c.ctx)) res := Protocol(C.gpgme_get_protocol(c.ctx))
runtime.KeepAlive(c)
return res
} }
func (c *Context) SetKeyListMode(m KeyListMode) error { func (c *Context) SetKeyListMode(m KeyListMode) error {
return handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m))) err := handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m)))
runtime.KeepAlive(c)
return err
} }
func (c *Context) KeyListMode() KeyListMode { func (c *Context) KeyListMode() KeyListMode {
return KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx))
runtime.KeepAlive(c)
return res
} }
// Unavailable in 1.3.2: // Unavailable in 1.3.2:
// func (c *Context) SetPinEntryMode(m PinEntryMode) error { // func (c *Context) SetPinEntryMode(m PinEntryMode) error {
// return handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) // err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m)))
// runtime.KeepAlive(c)
// return err
// } // }
// Unavailable in 1.3.2: // Unavailable in 1.3.2:
// func (c *Context) PinEntryMode() PinEntryMode { // func (c *Context) PinEntryMode() PinEntryMode {
// return PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) // res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx))
// runtime.KeepAlive(c)
// return res
// } // }
func (c *Context) SetCallback(callback Callback) error { func (c *Context) SetCallback(callback Callback) error {
@ -340,11 +376,17 @@ func (c *Context) SetCallback(callback Callback) error {
c.cbc = 0 c.cbc = 0
_, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0) _, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0)
} }
runtime.KeepAlive(c)
return err return err
} }
func (c *Context) EngineInfo() *EngineInfo { func (c *Context) EngineInfo() *EngineInfo {
return &EngineInfo{info: C.gpgme_ctx_get_engine_info(c.ctx)} cInfo := C.gpgme_ctx_get_engine_info(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing cInfo.
res := copyEngineInfo(cInfo)
runtime.KeepAlive(c) // for accesses to cInfo
return res
} }
func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error { func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@ -357,19 +399,23 @@ func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error
chome = C.CString(homeDir) chome = C.CString(homeDir)
defer C.free(unsafe.Pointer(chome)) defer C.free(unsafe.Pointer(chome))
} }
return handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome)) err := handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome))
runtime.KeepAlive(c)
return err
} }
func (c *Context) KeyListStart(pattern string, secretOnly bool) error { func (c *Context) KeyListStart(pattern string, secretOnly bool) error {
cpattern := C.CString(pattern) cpattern := C.CString(pattern)
defer C.free(unsafe.Pointer(cpattern)) defer C.free(unsafe.Pointer(cpattern))
err := C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)) err := handleError(C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)))
return handleError(err) runtime.KeepAlive(c)
return err
} }
func (c *Context) KeyListNext() bool { func (c *Context) KeyListNext() bool {
c.Key = newKey() c.Key = newKey()
err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k)) err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k))
runtime.KeepAlive(c) // implies runtime.KeepAlive(c.Key)
if err != nil { if err != nil {
if e, ok := err.(Error); ok && e.Code() == ErrorEOF { if e, ok := err.(Error); ok && e.Code() == ErrorEOF {
c.KeyError = nil c.KeyError = nil
@ -383,7 +429,9 @@ func (c *Context) KeyListNext() bool {
} }
func (c *Context) KeyListEnd() error { func (c *Context) KeyListEnd() error {
return handleError(C.gpgme_op_keylist_end(c.ctx)) err := handleError(C.gpgme_op_keylist_end(c.ctx))
runtime.KeepAlive(c)
return err
} }
func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
@ -391,7 +439,11 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
cfpr := C.CString(fingerprint) cfpr := C.CString(fingerprint)
defer C.free(unsafe.Pointer(cfpr)) defer C.free(unsafe.Pointer(cfpr))
err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret)))
if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF { runtime.KeepAlive(c)
runtime.KeepAlive(key)
keyKIsNil := key.k == nil
runtime.KeepAlive(key)
if e, ok := err.(Error); keyKIsNil && ok && e.Code() == ErrorEOF {
return nil, fmt.Errorf("key %q not found", fingerprint) return nil, fmt.Errorf("key %q not found", fingerprint)
} }
if err != nil { if err != nil {
@ -401,11 +453,19 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
} }
func (c *Context) Decrypt(ciphertext, plaintext *Data) error { func (c *Context) Decrypt(ciphertext, plaintext *Data) error {
return handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(ciphertext)
runtime.KeepAlive(plaintext)
return err
} }
func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error { func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error {
return handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(ciphertext)
runtime.KeepAlive(plaintext)
return err
} }
type Signature struct { type Signature struct {
@ -432,10 +492,20 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
plainPtr = plain.dh plainPtr = plain.dh
} }
err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr)) err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr))
runtime.KeepAlive(c)
runtime.KeepAlive(sig)
if signedText != nil {
runtime.KeepAlive(signedText)
}
if plain != nil {
runtime.KeepAlive(plain)
}
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
res := C.gpgme_op_verify_result(c.ctx) res := C.gpgme_op_verify_result(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing res.
sigs := []Signature{} sigs := []Signature{}
for s := res.signatures; s != nil; s = s.next { for s := res.signatures; s != nil; s = s.next {
sig := Signature{ sig := Signature{
@ -455,7 +525,9 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
} }
sigs = append(sigs, sig) sigs = append(sigs, sig)
} }
return C.GoString(res.file_name), sigs, nil fileName := C.GoString(res.file_name)
runtime.KeepAlive(c) // for all accesses to res above
return fileName, sigs, nil
} }
func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error { func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error {
@ -467,18 +539,116 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe
*ptr = recipients[i].k *ptr = recipients[i].k
} }
err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh) err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh)
runtime.KeepAlive(c)
runtime.KeepAlive(recipients)
runtime.KeepAlive(plaintext)
runtime.KeepAlive(ciphertext)
return handleError(err) return handleError(err)
} }
func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error { func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error {
C.gpgme_signers_clear(c.ctx) C.gpgme_signers_clear(c.ctx)
runtime.KeepAlive(c)
for _, k := range signers { for _, k := range signers {
if err := handleError(C.gpgme_signers_add(c.ctx, k.k)); err != nil { err := handleError(C.gpgme_signers_add(c.ctx, k.k))
runtime.KeepAlive(c)
runtime.KeepAlive(k)
if err != nil {
C.gpgme_signers_clear(c.ctx) C.gpgme_signers_clear(c.ctx)
runtime.KeepAlive(c)
return err return err
} }
} }
return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
runtime.KeepAlive(c)
runtime.KeepAlive(plain)
runtime.KeepAlive(sig)
return err
}
type AssuanDataCallback func(data []byte) error
type AssuanInquireCallback func(name, args string) error
type AssuanStatusCallback func(status, args string) error
// AssuanSend sends a raw Assuan command to gpg-agent
func (c *Context) AssuanSend(
cmd string,
data AssuanDataCallback,
inquiry AssuanInquireCallback,
status AssuanStatusCallback,
) error {
var operr C.gpgme_error_t
dataPtr := callbackAdd(&data)
inquiryPtr := callbackAdd(&inquiry)
statusPtr := callbackAdd(&status)
cmdCStr := C.CString(cmd)
defer C.free(unsafe.Pointer(cmdCStr))
err := C.gogpgme_op_assuan_transact_ext(
c.ctx,
cmdCStr,
C.uintptr_t(dataPtr),
C.uintptr_t(inquiryPtr),
C.uintptr_t(statusPtr),
&operr,
)
runtime.KeepAlive(c)
if handleError(operr) != nil {
return handleError(operr)
}
return handleError(err)
}
//export gogpgme_assuan_data_callback
func gogpgme_assuan_data_callback(handle unsafe.Pointer, data unsafe.Pointer, datalen C.size_t) C.gpgme_error_t {
c := callbackLookup(uintptr(handle)).(*AssuanDataCallback)
if *c == nil {
return 0
}
(*c)(C.GoBytes(data, C.int(datalen)))
return 0
}
//export gogpgme_assuan_inquiry_callback
func gogpgme_assuan_inquiry_callback(handle unsafe.Pointer, cName *C.char, cArgs *C.char) C.gpgme_error_t {
name := C.GoString(cName)
args := C.GoString(cArgs)
c := callbackLookup(uintptr(handle)).(*AssuanInquireCallback)
if *c == nil {
return 0
}
(*c)(name, args)
return 0
}
//export gogpgme_assuan_status_callback
func gogpgme_assuan_status_callback(handle unsafe.Pointer, cStatus *C.char, cArgs *C.char) C.gpgme_error_t {
status := C.GoString(cStatus)
args := C.GoString(cArgs)
c := callbackLookup(uintptr(handle)).(*AssuanStatusCallback)
if *c == nil {
return 0
}
(*c)(status, args)
return 0
}
// ExportModeFlags defines how keys are exported from Export
type ExportModeFlags uint
const (
ExportModeExtern ExportModeFlags = C.GPGME_EXPORT_MODE_EXTERN
ExportModeMinimal ExportModeFlags = C.GPGME_EXPORT_MODE_MINIMAL
)
func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error {
pat := C.CString(pattern)
defer C.free(unsafe.Pointer(pat))
err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(data)
return err
} }
// ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned". // ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned".
@ -517,10 +687,14 @@ type ImportResult struct {
func (c *Context) Import(keyData *Data) (*ImportResult, error) { func (c *Context) Import(keyData *Data) (*ImportResult, error) {
err := handleError(C.gpgme_op_import(c.ctx, keyData.dh)) err := handleError(C.gpgme_op_import(c.ctx, keyData.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(keyData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
res := C.gpgme_op_import_result(c.ctx) res := C.gpgme_op_import_result(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing res.
imports := []ImportStatus{} imports := []ImportStatus{}
for s := res.imports; s != nil; s = s.next { for s := res.imports; s != nil; s = s.next {
imports = append(imports, ImportStatus{ imports = append(imports, ImportStatus{
@ -529,7 +703,7 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
Status: ImportStatusFlags(s.status), Status: ImportStatusFlags(s.status),
}) })
} }
return &ImportResult{ importResult := &ImportResult{
Considered: int(res.considered), Considered: int(res.considered),
NoUserID: int(res.no_user_id), NoUserID: int(res.no_user_id),
Imported: int(res.imported), Imported: int(res.imported),
@ -544,11 +718,13 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
SecretUnchanged: int(res.secret_unchanged), SecretUnchanged: int(res.secret_unchanged),
NotImported: int(res.not_imported), NotImported: int(res.not_imported),
Imports: imports, Imports: imports,
}, nil }
runtime.KeepAlive(c) // for all accesses to res above
return importResult, nil
} }
type Key struct { type Key struct {
k C.gpgme_key_t k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C
} }
func newKey() *Key { func newKey() *Key {
@ -559,85 +735,122 @@ func newKey() *Key {
func (k *Key) Release() { func (k *Key) Release() {
C.gpgme_key_release(k.k) C.gpgme_key_release(k.k)
runtime.KeepAlive(k)
k.k = nil k.k = nil
} }
func (k *Key) Revoked() bool { func (k *Key) Revoked() bool {
return C.key_revoked(k.k) != 0 res := C.key_revoked(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Expired() bool { func (k *Key) Expired() bool {
return C.key_expired(k.k) != 0 res := C.key_expired(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Disabled() bool { func (k *Key) Disabled() bool {
return C.key_disabled(k.k) != 0 res := C.key_disabled(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Invalid() bool { func (k *Key) Invalid() bool {
return C.key_invalid(k.k) != 0 res := C.key_invalid(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanEncrypt() bool { func (k *Key) CanEncrypt() bool {
return C.key_can_encrypt(k.k) != 0 res := C.key_can_encrypt(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanSign() bool { func (k *Key) CanSign() bool {
return C.key_can_sign(k.k) != 0 res := C.key_can_sign(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanCertify() bool { func (k *Key) CanCertify() bool {
return C.key_can_certify(k.k) != 0 res := C.key_can_certify(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Secret() bool { func (k *Key) Secret() bool {
return C.key_secret(k.k) != 0 res := C.key_secret(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanAuthenticate() bool { func (k *Key) CanAuthenticate() bool {
return C.key_can_authenticate(k.k) != 0 res := C.key_can_authenticate(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) IsQualified() bool { func (k *Key) IsQualified() bool {
return C.key_is_qualified(k.k) != 0 res := C.key_is_qualified(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Protocol() Protocol { func (k *Key) Protocol() Protocol {
return Protocol(k.k.protocol) res := Protocol(k.k.protocol)
runtime.KeepAlive(k)
return res
} }
func (k *Key) IssuerSerial() string { func (k *Key) IssuerSerial() string {
return C.GoString(k.k.issuer_serial) res := C.GoString(k.k.issuer_serial)
runtime.KeepAlive(k)
return res
} }
func (k *Key) IssuerName() string { func (k *Key) IssuerName() string {
return C.GoString(k.k.issuer_name) res := C.GoString(k.k.issuer_name)
runtime.KeepAlive(k)
return res
} }
func (k *Key) ChainID() string { func (k *Key) ChainID() string {
return C.GoString(k.k.chain_id) res := C.GoString(k.k.chain_id)
runtime.KeepAlive(k)
return res
} }
func (k *Key) OwnerTrust() Validity { func (k *Key) OwnerTrust() Validity {
return Validity(k.k.owner_trust) res := Validity(k.k.owner_trust)
runtime.KeepAlive(k)
return res
} }
func (k *Key) SubKeys() *SubKey { func (k *Key) SubKeys() *SubKey {
if k.k.subkeys == nil { subKeys := k.k.subkeys
runtime.KeepAlive(k)
if subKeys == nil {
return nil return nil
} }
return &SubKey{k: k.k.subkeys, parent: k} return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid
} }
func (k *Key) UserIDs() *UserID { func (k *Key) UserIDs() *UserID {
if k.k.uids == nil { uids := k.k.uids
runtime.KeepAlive(k)
if uids == nil {
return nil return nil
} }
return &UserID{u: k.k.uids, parent: k} return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid
} }
func (k *Key) KeyListMode() KeyListMode { func (k *Key) KeyListMode() KeyListMode {
return KeyListMode(k.k.keylist_mode) res := KeyListMode(k.k.keylist_mode)
runtime.KeepAlive(k)
return res
} }
type SubKey struct { type SubKey struct {
@ -737,12 +950,3 @@ func (u *UserID) Comment() string {
func (u *UserID) Email() string { func (u *UserID) Email() string {
return C.GoString(u.u.email) return C.GoString(u.u.email)
} }
// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
// - and cgo can't be used in tests. So, provide this helper for test initialization.
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO")
defer C.free(unsafe.Pointer(v))
C.unsetenv(v)
}

18
vendor/github.com/mtrmac/gpgme/unset_agent_info.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
// +build !windows
package gpgme
// #include <stdlib.h>
import "C"
import (
"unsafe"
)
// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
// - and cgo can't be used in tests. So, provide this helper for test initialization.
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO")
defer C.free(unsafe.Pointer(v))
C.unsetenv(v)
}

View File

@ -0,0 +1,14 @@
package gpgme
// #include <stdlib.h>
import "C"
import (
"unsafe"
)
// unsetenv is not available in mingw
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO=")
defer C.free(unsafe.Pointer(v))
C.putenv(v)
}

View File

@ -10,11 +10,10 @@ import (
// BarOption is a function option which changes the default behavior of a bar. // BarOption is a function option which changes the default behavior of a bar.
type BarOption func(*bState) type BarOption func(*bState)
type mergeWrapper interface {
MergeUnwrap() []decor.Decorator
}
func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) { func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
type mergeWrapper interface {
MergeUnwrap() []decor.Decorator
}
for _, decorator := range decorators { for _, decorator := range decorators {
if mw, ok := decorator.(mergeWrapper); ok { if mw, ok := decorator.(mergeWrapper); ok {
*dest = append(*dest, mw.MergeUnwrap()...) *dest = append(*dest, mw.MergeUnwrap()...)

View File

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"time" "time"
"unicode/utf8" "unicode/utf8"
"github.com/acarl005/stripansi"
) )
const ( const (
@ -117,25 +119,29 @@ var (
// W represents width and C represents bit set of width related config. // W represents width and C represents bit set of width related config.
// A decorator should embed WC, to enable width synchronization. // A decorator should embed WC, to enable width synchronization.
type WC struct { type WC struct {
W int W int
C int C int
dynFormat string dynFormat string
staticFormat string wsync chan int
wsync chan int
} }
// FormatMsg formats final message according to WC.W and WC.C. // FormatMsg formats final message according to WC.W and WC.C.
// Should be called by any Decorator implementation. // Should be called by any Decorator implementation.
func (wc *WC) FormatMsg(msg string) string { func (wc *WC) FormatMsg(msg string) string {
var format string
runeCount := utf8.RuneCountInString(stripansi.Strip(msg))
ansiCount := utf8.RuneCountInString(msg) - runeCount
if (wc.C & DSyncWidth) != 0 { if (wc.C & DSyncWidth) != 0 {
wc.wsync <- utf8.RuneCountInString(msg)
max := <-wc.wsync
if (wc.C & DextraSpace) != 0 { if (wc.C & DextraSpace) != 0 {
max++ runeCount++
} }
return fmt.Sprintf(fmt.Sprintf(wc.dynFormat, max), msg) wc.wsync <- runeCount
max := <-wc.wsync
format = fmt.Sprintf(wc.dynFormat, ansiCount+max)
} else {
format = fmt.Sprintf(wc.dynFormat, ansiCount+wc.W)
} }
return fmt.Sprintf(wc.staticFormat, msg) return fmt.Sprintf(format, msg)
} }
// Init initializes width related config. // Init initializes width related config.
@ -145,7 +151,6 @@ func (wc *WC) Init() WC {
wc.dynFormat += "-" wc.dynFormat += "-"
} }
wc.dynFormat += "%ds" wc.dynFormat += "%ds"
wc.staticFormat = fmt.Sprintf(wc.dynFormat, wc.W)
if (wc.C & DSyncWidth) != 0 { if (wc.C & DSyncWidth) != 0 {
// it's deliberate choice to override wsync on each Init() call, // it's deliberate choice to override wsync on each Init() call,
// this way globals like WCSyncSpace can be reused // this way globals like WCSyncSpace can be reused

View File

@ -2,6 +2,7 @@ package decor
import ( import (
"fmt" "fmt"
"strings"
"unicode/utf8" "unicode/utf8"
) )
@ -28,10 +29,7 @@ func Merge(decorator Decorator, placeholders ...WC) Decorator {
if (wc.C & DSyncWidth) == 0 { if (wc.C & DSyncWidth) == 0 {
return decorator return decorator
} }
md.placeHolders[i] = &placeHolderDecorator{ md.placeHolders[i] = &placeHolderDecorator{wc.Init()}
WC: wc.Init(),
wch: make(chan int),
}
} }
return md return md
} }
@ -69,29 +67,40 @@ func (d *mergeDecorator) Base() Decorator {
func (d *mergeDecorator) Decor(st *Statistics) string { func (d *mergeDecorator) Decor(st *Statistics) string {
msg := d.Decorator.Decor(st) msg := d.Decorator.Decor(st)
msgLen := utf8.RuneCountInString(msg) msgLen := utf8.RuneCountInString(msg)
var space int
for _, ph := range d.placeHolders {
space += <-ph.wch
}
d.wc.wsync <- msgLen - space
max := <-d.wc.wsync
if (d.wc.C & DextraSpace) != 0 { if (d.wc.C & DextraSpace) != 0 {
max++ msgLen++
} }
return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+space), msg)
var total int
max := utf8.RuneCountInString(d.placeHolders[0].FormatMsg(""))
total += max
pw := (msgLen - max) / len(d.placeHolders)
rem := (msgLen - max) % len(d.placeHolders)
var diff int
for i := 1; i < len(d.placeHolders); i++ {
ph := d.placeHolders[i]
width := pw - diff
if (ph.WC.C & DextraSpace) != 0 {
width--
if width < 0 {
width = 0
}
}
max = utf8.RuneCountInString(ph.FormatMsg(strings.Repeat(" ", width)))
total += max
diff = max - pw
}
d.wc.wsync <- pw + rem
max = <-d.wc.wsync
return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+total), msg)
} }
type placeHolderDecorator struct { type placeHolderDecorator struct {
WC WC
wch chan int
} }
func (d *placeHolderDecorator) Decor(st *Statistics) string { func (d *placeHolderDecorator) Decor(_ *Statistics) string {
go func() {
d.wch <- utf8.RuneCountInString(d.FormatMsg(""))
}()
return "" return ""
} }

View File

@ -2,6 +2,7 @@ module github.com/vbauerster/mpb/v4
require ( require (
github.com/VividCortex/ewma v1.1.1 github.com/VividCortex/ewma v1.1.1
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 // indirect golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 // indirect
) )

View File

@ -1,5 +1,7 @@
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4= golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

View File

@ -18,9 +18,7 @@ func (prox *proxyReader) Read(p []byte) (n int, err error) {
prox.iT = time.Now() prox.iT = time.Now()
} }
if err == io.EOF { if err == io.EOF {
go func() { go prox.bar.SetTotal(0, true)
prox.bar.SetTotal(0, true)
}()
} }
return return
} }
@ -37,9 +35,7 @@ func (prox *proxyWriterTo) WriteTo(w io.Writer) (n int64, err error) {
prox.iT = time.Now() prox.iT = time.Now()
} }
if err == io.EOF { if err == io.EOF {
go func() { go prox.bar.SetTotal(0, true)
prox.bar.SetTotal(0, true)
}()
} }
return return
} }

9
vendor/modules.txt vendored
View File

@ -29,6 +29,8 @@ github.com/Microsoft/hcsshim/internal/wclayer
github.com/Microsoft/hcsshim/osversion github.com/Microsoft/hcsshim/osversion
# github.com/VividCortex/ewma v1.1.1 # github.com/VividCortex/ewma v1.1.1
github.com/VividCortex/ewma github.com/VividCortex/ewma
# github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/acarl005/stripansi
# github.com/beorn7/perks v1.0.1 # github.com/beorn7/perks v1.0.1
github.com/beorn7/perks/quantile github.com/beorn7/perks/quantile
# github.com/blang/semver v3.5.1+incompatible # github.com/blang/semver v3.5.1+incompatible
@ -80,7 +82,7 @@ github.com/containers/common/pkg/cgroups
github.com/containers/common/pkg/unshare github.com/containers/common/pkg/unshare
# github.com/containers/conmon v2.0.10+incompatible # github.com/containers/conmon v2.0.10+incompatible
github.com/containers/conmon/runner/config github.com/containers/conmon/runner/config
# github.com/containers/image/v5 v5.1.0 # github.com/containers/image/v5 v5.2.0
github.com/containers/image/v5/copy github.com/containers/image/v5/copy
github.com/containers/image/v5/directory github.com/containers/image/v5/directory
github.com/containers/image/v5/directory/explicitfilepath github.com/containers/image/v5/directory/explicitfilepath
@ -91,6 +93,7 @@ github.com/containers/image/v5/docker/policyconfiguration
github.com/containers/image/v5/docker/reference github.com/containers/image/v5/docker/reference
github.com/containers/image/v5/docker/tarfile github.com/containers/image/v5/docker/tarfile
github.com/containers/image/v5/image github.com/containers/image/v5/image
github.com/containers/image/v5/internal/iolimits
github.com/containers/image/v5/internal/pkg/keyctl github.com/containers/image/v5/internal/pkg/keyctl
github.com/containers/image/v5/internal/tmpdir github.com/containers/image/v5/internal/tmpdir
github.com/containers/image/v5/manifest github.com/containers/image/v5/manifest
@ -337,7 +340,7 @@ github.com/modern-go/reflect2
github.com/morikuni/aec github.com/morikuni/aec
# github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 # github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618
github.com/mrunalp/fileutils github.com/mrunalp/fileutils
# github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c # github.com/mtrmac/gpgme v0.1.1
github.com/mtrmac/gpgme github.com/mtrmac/gpgme
# github.com/onsi/ginkgo v1.11.0 # github.com/onsi/ginkgo v1.11.0
github.com/onsi/ginkgo github.com/onsi/ginkgo
@ -510,7 +513,7 @@ github.com/varlink/go/varlink/idl
github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/archive/tar
github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/asm
github.com/vbatts/tar-split/tar/storage github.com/vbatts/tar-split/tar/storage
# github.com/vbauerster/mpb/v4 v4.11.1 # github.com/vbauerster/mpb/v4 v4.11.2
github.com/vbauerster/mpb/v4 github.com/vbauerster/mpb/v4
github.com/vbauerster/mpb/v4/cwriter github.com/vbauerster/mpb/v4/cwriter
github.com/vbauerster/mpb/v4/decor github.com/vbauerster/mpb/v4/decor