Commit Graph

64 Commits

Author SHA1 Message Date
Derek McGowan
9c6e923101 Merge pull request #11484 from dmcgowan/retry-50x
Retry registry operations once on 50x if last host
2025-05-06 04:52:32 +00:00
Phil Estes
c213879967 Merge pull request #11770 from azr/log-fetch-parameters
fix multipart layer fetch in client: pass config down
2025-05-05 12:51:17 +00:00
Danny Canter
33ee060a35 Use Go 1.19 atomic wrappers everywhere
We've long been able to use these and they have a couple
great benefits:

1. Forces you to always access them atomically. With the pointer
variants it's completely valid to access the regular ol' int64/uint32
etc. without using the atomic.* methods. These wrappers don't provide
access to the underlying value so it forces correct usage always.

2. Conveys intent much better. Seeing the type be atomic.Int32 immediately
lets the reader know that this var will be used in a concurrent context,
and we no longer need comments like "this MUST be accessed atomically"
or similar.

Signed-off-by: Danny Canter <danny@dcantah.dev>
2025-05-03 03:46:20 -07:00
Derek McGowan
d70d6245f0 Retry registry operations once on 50x on last host
Signed-off-by: Derek McGowan <derek@mcg.dev>
2025-04-30 12:19:06 -07:00
Adrien Delorme
89780188f7 dockerFetcher.open: show all parameters when pulling a layer
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
2025-04-30 09:09:42 +02:00
Adrien Delorme
83ad3b55fb code review fixes
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
2025-04-24 21:08:41 +02:00
Adrien Delorme
a196ee66ae better race mgt
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
2025-04-24 14:29:26 +02:00
Adrien Delorme
72c8c7708c only keep one setting: concurrent_layer_fetch_buffer
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
2025-04-24 11:41:33 +02:00
Adrien Delorme
024775dab1 set dl options on resolver
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
2025-04-24 11:41:33 +02:00
Adrien Delorme
755a4ac6f8 update
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
2025-04-24 11:39:42 +02:00
Adrien Delorme
f9af08820b perf(pull): multipart layer fetch
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
Co-Authored-By: Corentin REGAL <143578+co42@users.noreply.github.com>
2025-04-24 11:39:42 +02:00
Derek McGowan
ab41e7284c Merge pull request #11709 from fiam/alberto/fix-pusher-authorizer-error-on-mount
Fix cross-repo mount fallback after authorization failure
2025-04-17 23:24:13 +00:00
Maksym Pavlenko
adc1066168 Merge pull request #11092 from djdongjin/follow-hosts-header-in-docker-authorizer
Add cri headers and hosts.toml header to auth requests
2025-04-17 19:46:44 +00:00
Alberto Garcia Hierro
01ff3b3643 chore: fix broken links to https://docs.docker.com/registry
https://docs.docker.com/registry is deprecated and doesn't have that content
anymore, link to https://distribution.github.io/distribution instead.

Signed-off-by: Alberto Garcia Hierro <damaso.hierro@docker.com>
2025-04-17 11:20:23 +01:00
Alberto Garcia Hierro
40b0083c47 fix(docker pusher): if authorizing a cross-repo mount fails, fall back
Instead of failing and returning the error received from the authorizer,
fall back the already existing path to handle non-nil response with a
status code indicating a failure.

Allow the Authorizer to return an error both from Authorize and from
AddResponses.

Signed-off-by: Alberto Garcia Hierro <damaso.hierro@docker.com>
2025-04-17 11:05:38 +01:00
Derek McGowan
757c0bd9bf Merge pull request #11681 from AkihiroSuda/remove-schema1-2.1
Remove the support for Schema 1 images
2025-04-16 23:28:33 +00:00
Akihiro Suda
d9c889568e Remove the support for Schema 1 images
Schema 1 (`application/vnd.docker.distribution.manifest.v1+prettyjws`) has been
officially deprecated since containerd v1.7 (PR 6884), and disabled since v2.0 (PR 9765).

Users who have been seeing warnings like `conversion from schema 1 images is deprecated`
now have to rebuild the image with Schema 2 or OCI.

Schema 2 was introduced in Docker 1.10 (Feb 2016), so most users should have been already
using Schema 2 or OCI.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2025-04-11 09:03:26 +09:00
Cesar Talledo
3251e2cc8d Prevent panic in Docker pusher.
Prevent a panic in the Docker pusher pushWriter, by checking that
the pipe is non nil before attempting to use it.

The panic was found by Moby issue #46746 (https://github.com/moby/moby/issues/46746).
With this fix the panic no longer reproduces.

Signed-off-by: Cesar Talledo <cesar.talledo@docker.com>
2025-04-08 18:12:17 -07:00
Jin Dong
4857de8537 Add cri.config.headers to auth requests
Currently `cri.config.headers` is only added
to registry requests (manifests/blobs), but not
the auth requests.

This patch fixed it by passing `cri.config.headers`
when we create `DockerAuthorizer`.

Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2025-04-08 20:36:03 -04:00
Jin Dong
10b4eb4a90 Add hosts.toml headers to auth requests
This patch fixed an issue that when we create
a `DockerAuthorizer`, we didn't pass the
headers read from hosts.toml to it, causing
the headers are missing in auth requests.

Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2025-04-08 20:26:42 -04:00
Jin Dong
5ae698235b Only add containerd as User-Agent when it's empty
Go only allows one User-Agent, so we should use
containerd/version as User-Agent only if it's
empty.

ref: https://github.com/golang/go/blob/go1.23.0/src/net/http/h2_bundle.go#L9188-L9191

Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2025-04-08 20:16:07 -04:00
Jin Dong
53eec6c783 move host tlsconfig update to a separate function
Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2025-03-15 01:36:25 +00:00
Jin Dong
f702bf9fe5 [hosts] wrong explicitTLS value when dialTimeout is set
If `host.caCerts, host.clientPairs, host.skipVerify` are
all `nil` and `host.skipVerify` not `nil`, `explicitTLS`
will be set to be `true` instead of `false`.

Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2025-03-15 01:25:00 +00:00
Philip Laine
c4982bffc6 Add dial timeout field to hosts toml configuration
Signed-off-by: Philip Laine <philip.laine@gmail.com>
2025-03-13 23:33:53 -07:00
Akihiro Suda
618a832d0c Merge pull request #11327 from vvoland/image-children-attestation
Don't produce unnecessary logs when encountering attestations
2025-03-08 05:00:27 +00:00
Adrien Delorme
c51f5d26f1 perf(zstd): deactivate the low mem decoder
Signed-off-by: Adrien Delorme <azr@users.noreply.github.com>
Co-Authored-By: Corentin REGAL <143578+co42@users.noreply.github.com>
2025-02-05 14:39:16 +01:00
Paweł Gronowski
3cdfc1003d core/remotes: Handle attestations in MakeRefKey
Don't produce `reference for unknown type: application/vnd.in-toto+json`
warning logs when pushing/fetching an image containing the attestation
manifests.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-01-31 16:39:43 +01:00
Derek McGowan
ccb4c7429e Merge pull request #11167 from djdongjin/unify-resolver-transport
Unify default transport in docker resolver
2025-01-10 23:22:35 +00:00
Kazuyoshi Kato
5ad6a150b6 Merge pull request #11189 from djdongjin/move-to-go-native-fuzz
Move fuzz tests to go native fuzz [part1]
2025-01-10 01:27:14 +00:00
Jin Dong
a4e3218e8f change tmp dir creation in fuzz to t.TempDir
Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2025-01-02 20:26:07 +00:00
Jin Dong
2de1030299 Move fuzz tests to go native fuzz [part1]
Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2024-12-24 22:48:20 +00:00
Austin Vazquez
bee64b2b93 Remove loop variable copies
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2024-12-23 23:14:49 -07:00
Jin Dong
47c4dba409 Unify default transport in docker resolver
The default transport are used in 3 places:

1. `ConfigureDefaultRegistries` (no `hosts_dir` is set)
2. `ConfigureHosts` (when `hosts_dir` is set)
3. in cri service

2 and 3 use/duplicate the same default transport, whereas 1
uses go's default Client/Transport

This PR moves the default transport to a common funcion (can pass in tls config).

Signed-off-by: Jin Dong <djdongjin95@gmail.com>
2024-12-16 04:00:01 +00:00
Sebastiaan van Stijn
700b90618d resolver/docker: fix confusing "trying next host" log
The resolver uses some log messages to indicate the next host will be used,
however it would print this message even if no next host was available
to try.

This patch changes the log messages to indicate the action taken, which is
either "fetch failed" or if there's no other hosts to try, "trying next host".

While updating, also slightly updated the logs for consistency.

Before this patch:

    DEBU[2024-10-23T20:49:29.640581097Z] resolving                                     host=registry-1.docker.io spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6
    DEBU[2024-10-23T20:49:29.640632763Z] do request                                    host=registry-1.docker.io request.header.accept="application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*" request.header.user-agent="docker/27.3.1 go/go1.22.7 git-commit/41ca978 kernel/6.10.11-linuxkit os/linux arch/arm64 containerd-client/1.7.22+unknown storage-driver/overlayfs UpstreamClient(Docker-Client/27.3.1 \\(linux\\))" request.method=HEAD spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6 url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T20:49:29.979613013Z] fetch response received                       host=registry-1.docker.io response.header.content-length=162 response.header.content-type=application/json response.header.date="Wed, 23 Oct 2024 20:49:29 GMT" response.header.docker-distribution-api-version=registry/2.0 response.header.docker-ratelimit-source=94.210.180.92 response.header.strict-transport-security="max-age=31536000" response.header.www-authenticate="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\"" response.status="401 Unauthorized" spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6 url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T20:49:29.979907138Z] Unauthorized                                  header="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\"" host=registry-1.docker.io spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6
    DEBU[2024-10-23T20:49:29.980038430Z] do request                                    host=registry-1.docker.io request.header.accept="application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*" request.header.user-agent="docker/27.3.1 go/go1.22.7 git-commit/41ca978 kernel/6.10.11-linuxkit os/linux arch/arm64 containerd-client/1.7.22+unknown storage-driver/overlayfs UpstreamClient(Docker-Client/27.3.1 \\(linux\\))" request.method=HEAD spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6 url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T20:49:30.466825972Z] fetch response received                       host=registry-1.docker.io response.header.content-length=162 response.header.content-type=application/json response.header.date="Wed, 23 Oct 2024 20:49:30 GMT" response.header.docker-distribution-api-version=registry/2.0 response.header.docker-ratelimit-source=94.210.180.92 response.header.strict-transport-security="max-age=31536000" response.header.www-authenticate="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\",error=\"insufficient_scope\"" response.status="401 Unauthorized" spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6 url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T20:49:30.467056055Z] Unauthorized                                  header="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\",error=\"insufficient_scope\"" host=registry-1.docker.io spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6
    INFO[2024-10-23T20:49:30.467273305Z] trying next host                              error="pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed" host=registry-1.docker.io spanID=6e2b9c871009cfd9 traceID=6c65b97879548fce4372faa8375d17e6

With this patch:

    DEBU[2024-10-23T21:17:53.487428843Z] resolving                                     host=registry-1.docker.io spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db
    DEBU[2024-10-23T21:17:53.487498968Z] do request                                    host=registry-1.docker.io request.header.accept="application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*" request.header.user-agent="docker/dev go/go1.22.8 git-commit/06c2ba1fa02626e242dc8dfe888f022bcd247c52 kernel/6.10.11-linuxkit os/linux arch/arm64 containerd-client/1.7.22+unknown storage-driver/overlayfs UpstreamClient(Docker-Client/27.3.1 \\(darwin\\))" request.method=HEAD spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T21:17:53.832270052Z] fetch response received                       host=registry-1.docker.io response.header.content-length=162 response.header.content-type=application/json response.header.date="Wed, 23 Oct 2024 21:17:53 GMT" response.header.docker-distribution-api-version=registry/2.0 response.header.docker-ratelimit-source=94.210.180.92 response.header.strict-transport-security="max-age=31536000" response.header.www-authenticate="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\"" response.status="401 Unauthorized" spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T21:17:53.832843177Z] Unauthorized                                  header="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\"" host=registry-1.docker.io spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db
    DEBU[2024-10-23T21:17:53.833364760Z] do request                                    host=registry-1.docker.io request.header.accept="application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*" request.header.user-agent="docker/dev go/go1.22.8 git-commit/06c2ba1fa02626e242dc8dfe888f022bcd247c52 kernel/6.10.11-linuxkit os/linux arch/arm64 containerd-client/1.7.22+unknown storage-driver/overlayfs UpstreamClient(Docker-Client/27.3.1 \\(darwin\\))" request.method=HEAD spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T21:17:54.345483219Z] fetch response received                       host=registry-1.docker.io response.header.content-length=162 response.header.content-type=application/json response.header.date="Wed, 23 Oct 2024 21:17:54 GMT" response.header.docker-distribution-api-version=registry/2.0 response.header.docker-ratelimit-source=4203339e-74c0-11e4-bea4-0242ac11001b response.header.strict-transport-security="max-age=31536000" response.header.www-authenticate="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\",error=\"insufficient_scope\"" response.status="401 Unauthorized" spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db url="https://registry-1.docker.io/v2/library/nosuchimage/manifests/latest"
    DEBU[2024-10-23T21:17:54.345601386Z] Unauthorized                                  header="Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/nosuchimage:pull\",error=\"insufficient_scope\"" host=registry-1.docker.io spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db
    INFO[2024-10-23T21:17:54.345801761Z] fetch failed                                  error="pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed" host=registry-1.docker.io spanID=4a74f71b08243447 traceID=fc1177ef212d33617c883842b9b9b8db

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-26 14:16:00 +02:00
Akhil Mohan
ebc47359ea use format string when using printf like commands
As per https://github.com/golang/go/issues/60529, printf like commands with
non-constant format strings and no args give an error in govet

Signed-off-by: Akhil Mohan <akhilerm@gmail.com>
2024-08-14 17:04:53 +05:30
Fu Wei
0975ec0908 Merge pull request #10342 from dmcgowan/add-mutex-fallback-host
Adds a mutex to protect fallback host
2024-06-17 13:13:57 +00:00
Maksym Pavlenko
ab61734e3d Merge pull request #10231 from jedevc/add-get-token-span
auth: add span to FetchToken helpers
2024-06-14 19:33:52 +00:00
Derek McGowan
38e2f00382 Adds a mutex to protect fallback host
Race detector complains about concurrent access such as with Dispatch on
push.

Signed-off-by: Derek McGowan <derek@mcg.dev>
2024-06-14 10:43:39 -07:00
Derek McGowan
d23c4b8b53 Use unix and windows specific connection error checks
Signed-off-by: Derek McGowan <derek@mcg.dev>
2024-06-05 14:39:57 -07:00
Derek McGowan
02b6c6939f Allow fallback across default ports
When no port is specified, allow falling back from 443 to 80 when
http is specified along with a TLS configuration.

Signed-off-by: Derek McGowan <derek@mcg.dev>
2024-06-05 14:39:57 -07:00
Justin Chadwell
9831a62d72 auth: add span to FetchToken helpers
Before this, during a call to the docker resolver, we would generate
span wrappers for each HTTPRequest correctly, however, as the docker
resolver reaches out to the docker authorizer, it could create HTTP
requests (for fetching tokens) that would not be wrapped in any span.

This can result in rather confusing traces, e.g. something like:

	remotes.docker.resolver.HTTPRequest
		HTTP HEAD (fetch index, fails with 401)
	HTTP GET (fetch token)
	remotes.docker.resolver.HTTPRequest
		HTTP HEAD (fetch index)
	remotes.docker.resolver.HTTPRequest
		HTTP GET (fetch manifest)

By adding a span into the FetchToken, this trace becomes a little easier
to consume:

	remotes.docker.resolver.HTTPRequest
		HTTP HEAD (fetch index, fails with 401)
	remotes.docker.resolver.FetchToken
		HTTP GET (fetch token)
	remotes.docker.resolver.HTTPRequest
		HTTP HEAD (fetch index)
	remotes.docker.resolver.HTTPRequest
		HTTP GET (fetch manifest)

Signed-off-by: Justin Chadwell <me@jedevc.com>
2024-05-15 15:54:37 +01:00
zouyee
11d8beff80 optimize error logs by providing absolute file paths
Signed-off-by: zouyee <zouyee1989@gmail.com>
2024-04-30 09:08:01 +08:00
Derek McGowan
5e470e1cae Update HTTPFallback to handle tls handshake timeout
Signed-off-by: Derek McGowan <derek@mcg.dev>
2024-04-22 18:53:27 -07:00
Kohei Tokunaga
63d5573a38 remote: Fix HTTPFallback fails when pushing manifest
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
2024-04-03 00:28:31 +09:00
Derek McGowan
b8654e36f4 Merge pull request #8379 from jedevc/docker-pusher-concurrency
Fix various timing issues with docker pusher
2024-02-21 17:59:56 +00:00
Akihiro Suda
99721c27e1 Disable the support for Schema 1 images
Schema 1 (`application/vnd.docker.distribution.manifest.v1+prettyjws`) has been
officially deprecated since containerd v1.7 (PR 6884).

We have planned to remove the support for Schema 1 in containerd v2.0, but this
removal may still surprise some users.
So, in containerd v2.0 we will just disable it by default.

The support for Schema 1 can be still enabled by setting an environment variable
`CONTAINERD_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE=1`, however, this workaround
will be completely removed in containerd v2.1.

Schema 2 was introduced in Docker 1.10 (Feb 2016), so most users should
have been already using Schema 2 or OCI.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2024-02-15 11:11:35 +09:00
Justin Chadwell
a9152ebf89 copy: prevent potential deadlock if close before fully written
We also need an additional check to avoid setting both the error and
response which can create a race where they can arrive in the receiving
thread in either order.

If we hit an error, we don't need to send the response.

> There is a condition where the registry (unexpectedly, not to spec)
> returns 201 or 204 on the put before the body is fully written. I would
> expect that the http library would issue close and could fall into a
> deadlock here. We could just read respC and call setResponse. In that
> case ErrClosedPipe would get returned and Commit shouldn't be called
> anyway.

Signed-off-by: Justin Chadwell <me@jedevc.com>
2024-02-03 10:20:11 +01:00
Justin Chadwell
b48e1141eb copy: setError should imply Close
If sending two messages from goroutine X:

	a <- 1
	b <- 2

And receiving them in goroutine Y:

	select {
	case <- a:
	case <- b:
	}

Either branch of the select can trigger first - so when we call
.setError and .Close next to each other, we don't know whether the done
channel will close first or the error channel will receive first - so
sometimes, we get an incorrect error message.

We resolve this by not sending both signals - instead, we can have
.setError *imply* .Close, by having the pushWriter call .Close on
itself, after receiving an error.

Signed-off-by: Justin Chadwell <me@jedevc.com>
2024-02-03 10:20:11 +01:00
Justin Chadwell
651cfa2a2c pushWriter: refactor reset pipe logic into separate function
Signed-off-by: Justin Chadwell <me@jedevc.com>
2024-02-03 10:18:05 +01:00
Justin Chadwell
9d7641ff3e copy: improve error detection from closed pipes
If we get io.ErrClosedPipe in pushWriter.Write, there are three possible
scenarios:

- The request has failed, we need to attempt a reset, so we can expect a
  new pipe incoming on pipeC.
- The request has failed, we don't need to attempt a reset, so we can
  expect an incoming error on errC.
- Something else externally has called Close, so we can expect the done
  channel to be closed.

This patch ensures that we block for as long as possible (while still
handling each of the above cases, so we avoid hanging), to make sure
that we properly return an appropriate error message each time.

Signed-off-by: Justin Chadwell <me@jedevc.com>
2024-02-03 10:18:05 +01:00