Repository: restic/restic
Pull request #5299 (2025-03-23 18:58:57)

A few more small cleanups that should not change behaviour.
What does this PR change? What problem does it solve?
-----------------------------------------------------

Fixes a few more minor problems. Commits are independent fixes.

Was the change previously discussed in an issue or on the forum?
----------------------------------------------------------------

No. Small cleanups only.

Checklist
---------

Please always follow these steps:
- Read the [contribution guidelines](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#providing-patches).
- Enable [maintainer edits](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork).
- Run `gofmt` on the code in all commits.
- Format all commit messages in the same style as [the other commits in the repository](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#git-commits).
-->

- [x] I have added tests for all code changes. No behaviour changes.
- [x] I have added documentation for relevant changes (in the manual). Not user visible.
- [x] There's a new file in `changelog/unreleased/` that describes the changes for our users (see [template](https://github.com/restic/restic/blob/master/changelog/TEMPLATE)). Not user visible.
- [x] I'm done! This pull request is ready for review.



Patch:
diff --git a/build.go b/build.go
index 0f4e80c91..32eefb7c0 100644
--- a/build.go
+++ b/build.go
@@ -58,7 +58,7 @@ var config = Config{
 	Main:             "./cmd/restic",                           // package name for the main package
 	DefaultBuildTags: []string{"selfupdate"},                   // specify build tags which are always used
 	Tests:            []string{"./..."},                        // tests to run
-	MinVersion:       GoVersion{Major: 1, Minor: 23, Patch: 0}, // minimum Go version supported
+	MinVersion:       GoVersion{Major: 1, Minor: 22, Patch: 0}, // minimum Go version supported
 }
 
 // Config configures the build.
@@ -382,6 +382,12 @@ func main() {
 		}
 	}
 
+	solarisMinVersion := GoVersion{Major: 1, Minor: 20, Patch: 0}
+	if env["GOARCH"] == "solaris" && !goVersion.AtLeast(solarisMinVersion) {
+		fmt.Fprintf(os.Stderr, "Detected version %s is too old, restic requires at least %s for Solaris\n", goVersion, solarisMinVersion)
+		os.Exit(1)
+	}
+
 	verbosePrintf("detected Go version %v\n", goVersion)
 
 	preserveSymbols := false
diff --git a/changelog/unreleased/pull-4938 b/changelog/unreleased/pull-4938
index 831997a4a..d95bca16d 100644
--- a/changelog/unreleased/pull-4938
+++ b/changelog/unreleased/pull-4938
@@ -1,11 +1,9 @@
-Change: Update dependencies and require Go 1.23 or newer
+Change: Update dependencies and require Go 1.22 or newer
 
-We have updated all dependencies. Since some libraries require newer Go
-standard library features, support for Go 1.19, 1.20, 1.21 and 1.22 has been
-dropped, which means that restic now requires at least Go 1.23 to build.
+We have updated all dependencies. Since some libraries require newer Go standard
+library features, support for Go 1.19, 1.20 and 1.21 has been dropped, which means
+that restic now requires at least Go 1.22 to build.
 
-This also disables support for TLS versions older than TLS 1.2. On Windows,
-restic now requires at least Windows 10 or Windows Server 2016. On macOS,
-restic now requires at least macOS 11 Big Sur.
+This also disables support for TLS versions older than TLS 1.2.
 
 https://github.com/restic/restic/pull/4938
diff --git a/cmd/restic/global.go b/cmd/restic/global.go
index 22b09390c..4d2ea75d5 100644
--- a/cmd/restic/global.go
+++ b/cmd/restic/global.go
@@ -302,7 +302,7 @@ func resolvePassword(opts *GlobalOptions, envStr string) (string, error) {
 		if err != nil {
 			return "", err
 		}
-		return (strings.TrimSpace(string(output))), nil
+		return strings.TrimSpace(string(output)), nil
 	}
 	if opts.PasswordFile != "" {
 		return loadPasswordFromFile(opts.PasswordFile)
diff --git a/doc/020_installation.rst b/doc/020_installation.rst
index a39ca896b..a53888221 100644
--- a/doc/020_installation.rst
+++ b/doc/020_installation.rst
@@ -284,7 +284,7 @@ From Source
 ***********
 
 restic is written in the Go programming language and you need at least
-Go version 1.23. Building restic may also work with older versions of Go,
+Go version 1.22. Building restic may also work with older versions of Go,
 but that's not supported. See the `Getting
 started <https://go.dev/doc/install>`__ guide of the Go project for
 instructions how to install Go.
diff --git a/go.mod b/go.mod
index 153b56159..f4823c674 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,6 @@
 module github.com/restic/restic
 
-go 1.23
-
-// keep the old behavior for reparse points on windows until handling reparse points has been improved in restic
-// https://forum.restic.net/t/windows-junction-backup-with-go1-23-or-later/8940
-godebug winsymlink=0
+go 1.22
 
 require (
 	cloud.google.com/go/storage v1.43.0
@@ -21,7 +17,7 @@ require (
 	github.com/google/go-cmp v0.6.0
 	github.com/hashicorp/golang-lru/v2 v2.0.7
 	github.com/klauspost/compress v1.18.0
-	github.com/minio/minio-go/v7 v7.0.88
+	github.com/minio/minio-go/v7 v7.0.77
 	github.com/ncw/swift/v2 v2.0.3
 	github.com/peterbourgon/unixtransport v0.0.4
 	github.com/pkg/errors v0.9.1
@@ -58,7 +54,7 @@ require (
 	github.com/go-ini/ini v1.67.0 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
-	github.com/goccy/go-json v0.10.5 // indirect
+	github.com/goccy/go-json v0.10.3 // indirect
 	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
@@ -67,10 +63,9 @@ require (
 	github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
 	github.com/googleapis/gax-go/v2 v2.14.1 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.9 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.8 // indirect
 	github.com/kr/fs v0.1.0 // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
-	github.com/minio/crc64nvme v1.0.1 // indirect
 	github.com/minio/md5-simd v1.1.2 // indirect
 	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
 	github.com/rs/xid v1.6.0 // indirect
diff --git a/go.sum b/go.sum
index d428914d4..2f0ca42c6 100644
--- a/go.sum
+++ b/go.sum
@@ -86,8 +86,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
 github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
-github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
-github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
 github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -139,8 +139,8 @@ github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeW
 github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
 github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
 github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
-github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
+github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
+github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -154,12 +154,10 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
-github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
-github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.88 h1:v8MoIJjwYxOkehp+eiLIuvXk87P2raUtoU5klrAAshs=
-github.com/minio/minio-go/v7 v7.0.88/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg=
+github.com/minio/minio-go/v7 v7.0.77 h1:GaGghJRg9nwDVlNbwYjSDJT1rqltQkBFDsypWX1v3Bw=
+github.com/minio/minio-go/v7 v7.0.77/go.mod h1:AVM3IUN6WwKzmwBxVdjzhH8xq+f57JSbbvzqvUzR6eg=
 github.com/ncw/swift/v2 v2.0.3 h1:8R9dmgFIWs+RiVlisCEfiQiik1hjuR0JnOkLxaP9ihg=
 github.com/ncw/swift/v2 v2.0.3/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
 github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go
index 0b71cbacf..4a6577d27 100644
--- a/internal/archiver/archiver.go
+++ b/internal/archiver/archiver.go
@@ -695,10 +695,6 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree,
 				return futureNode{}, 0, err
 			}
 
-			if err != nil {
-				return futureNode{}, 0, err
-			}
-
 			if !excluded {
 				nodes = append(nodes, fn)
 			}
diff --git a/internal/backend/http_transport.go b/internal/backend/http_transport.go
index 5a3856e41..4e0bb6594 100644
--- a/internal/backend/http_transport.go
+++ b/internal/backend/http_transport.go
@@ -80,7 +80,6 @@ func Transport(opts TransportOptions) (http.RoundTripper, error) {
 		DialContext: (&net.Dialer{
 			Timeout:   30 * time.Second,
 			KeepAlive: 30 * time.Second,
-			DualStack: true,
 		}).DialContext,
 		MaxIdleConns:          100,
 		MaxIdleConnsPerHost:   100,
diff --git a/internal/backend/s3/s3.go b/internal/backend/s3/s3.go
index 3653c827c..2eded428c 100644
--- a/internal/backend/s3/s3.go
+++ b/internal/backend/s3/s3.go
@@ -131,11 +131,14 @@ func getCredentials(cfg Config, tr http.RoundTripper) (*credentials.Credentials,
 		&credentials.EnvMinio{},
 		&credentials.FileAWSCredentials{},
 		&credentials.FileMinioClient{},
-		&credentials.IAM{},
+		&credentials.IAM{
+			Client: &http.Client{
+				Transport: tr,
+			},
+		},
 	})
-	client := &http.Client{Transport: tr}
 
-	c, err := creds.GetWithContext(&credentials.CredContext{Client: client})
+	c, err := creds.Get()
 	if err != nil {
 		return nil, errors.Wrap(err, "creds.Get")
 	}
diff --git a/internal/repository/index/index.go b/internal/repository/index/index.go
index 5a80a3c10..5729a689c 100644
--- a/internal/repository/index/index.go
+++ b/internal/repository/index/index.go
@@ -93,8 +93,8 @@ const (
 	indexMaxAge   = 10 * time.Minute
 )
 
-// IndexFull returns true iff the index is "full enough" to be saved as a preliminary index.
-var IndexFull = func(idx *Index) bool {
+// Full returns true iff the index is "full enough" to be saved as a preliminary index.
+var Full = func(idx *Index) bool {
 	idx.m.RLock()
 	defer idx.m.RUnlock()
 
@@ -119,7 +119,7 @@ var IndexFull = func(idx *Index) bool {
 	return false
 }
 
-var IndexOversized = func(idx *Index) bool {
+var Oversized = func(idx *Index) bool {
 	idx.m.RLock()
 	defer idx.m.RUnlock()
 
@@ -258,8 +258,8 @@ func (idx *Index) EachByPack(ctx context.Context, packBlacklist restic.IDSet) <-
 		for packID, packByType := range byPack {
 			var result EachByPackResult
 			result.PackID = packID
-			for typ, pack := range packByType {
-				for _, e := range pack {
+			for typ, p := range packByType {
+				for _, e := range p {
 					result.Blobs = append(result.Blobs, idx.toPackedBlob(e, restic.BlobType(typ)).Blob)
 				}
 			}
@@ -511,10 +511,10 @@ func DecodeIndex(buf []byte, id restic.ID) (idx *Index, err error) {
 	}
 
 	idx = NewIndex()
-	for _, pack := range idxJSON.Packs {
-		packID := idx.addToPacks(pack.ID)
+	for _, p := range idxJSON.Packs {
+		packID := idx.addToPacks(p.ID)
 
-		for _, blob := range pack.Blobs {
+		for _, blob := range p.Blobs {
 			idx.store(packID, restic.Blob{
 				BlobHandle: restic.BlobHandle{
 					Type: blob.Type,
diff --git a/internal/repository/index/master_index.go b/internal/repository/index/master_index.go
index 2a2f4988b..a41119b58 100644
--- a/internal/repository/index/master_index.go
+++ b/internal/repository/index/master_index.go
@@ -211,7 +211,7 @@ func (mi *MasterIndex) finalizeFullIndexes() []*Index {
 			continue
 		}
 
-		if IndexFull(idx) {
+		if Full(idx) {
 			debug.Log("index %p is full", idx)
 			idx.Finalize()
 			list = append(list, idx)
@@ -419,7 +419,7 @@ func (mi *MasterIndex) Rewrite(ctx context.Context, repo restic.Unpacked[restic.
 		newIndex := NewIndex()
 		for task := range rewriteCh {
 			// always rewrite indexes that include a pack that must be removed or that are not full
-			if len(task.idx.Packs().Intersect(excludePacks)) == 0 && IndexFull(task.idx) && !IndexOversized(task.idx) {
+			if len(task.idx.Packs().Intersect(excludePacks)) == 0 && Full(task.idx) && !Oversized(task.idx) {
 				// make sure that each pack is only stored exactly once in the index
 				excludePacks.Merge(task.idx.Packs())
 				// index is already up to date
@@ -435,7 +435,7 @@ func (mi *MasterIndex) Rewrite(ctx context.Context, repo restic.Unpacked[restic.
 
 			for pbs := range task.idx.EachByPack(wgCtx, excludePacks) {
 				newIndex.StorePack(pbs.PackID, pbs.Blobs)
-				if IndexFull(newIndex) {
+				if Full(newIndex) {
 					select {
 					case saveCh <- newIndex:
 					case <-wgCtx.Done():
@@ -532,7 +532,7 @@ func (mi *MasterIndex) SaveFallback(ctx context.Context, repo restic.SaverRemove
 			for pbs := range idx.EachByPack(wgCtx, excludePacks) {
 				newIndex.StorePack(pbs.PackID, pbs.Blobs)
 				p.Add(1)
-				if IndexFull(newIndex) {
+				if Full(newIndex) {
 					select {
 					case ch <- newIndex:
 					case <-wgCtx.Done():
diff --git a/internal/repository/prune.go b/internal/repository/prune.go
index e22dd4c20..9726a6032 100644
--- a/internal/repository/prune.go
+++ b/internal/repository/prune.go
@@ -276,7 +276,7 @@ func packInfoFromIndex(ctx context.Context, idx restic.ListBlobser, usedBlobs *i
 			ip := indexPack[blob.PackID]
 			size := uint64(blob.Length)
 			switch {
-			case ip.usedBlobs > 0, (ip.duplicateBlobs == ip.unusedBlobs), count == 0:
+			case ip.usedBlobs > 0, ip.duplicateBlobs == ip.unusedBlobs, count == 0:
 				// other used blobs in pack, only duplicate blobs or "last" occurrence ->  transition to used
 				// a pack file created by an interrupted prune run will consist of only duplicate blobs
 				// thus select such already repacked pack files
diff --git a/internal/repository/warmup.go b/internal/repository/warmup.go
index 7d96185a7..eca46692a 100644
--- a/internal/repository/warmup.go
+++ b/internal/repository/warmup.go
@@ -23,7 +23,7 @@ func (job *WarmupJob) Wait(ctx context.Context) error {
 }
 
 // StartWarmup creates a new warmup job, requesting the backend to warmup the specified packs.
-func (repo *Repository) StartWarmup(ctx context.Context, packs restic.IDSet) (restic.WarmupJob, error) {
+func (r *Repository) StartWarmup(ctx context.Context, packs restic.IDSet) (restic.WarmupJob, error) {
 	handles := make([]backend.Handle, 0, len(packs))
 	for pack := range packs {
 		handles = append(
@@ -31,9 +31,9 @@ func (repo *Repository) StartWarmup(ctx context.Context, packs restic.IDSet) (re
 			backend.Handle{Type: restic.PackFile, Name: pack.String()},
 		)
 	}
-	handlesWarmingUp, err := repo.be.Warmup(ctx, handles)
+	handlesWarmingUp, err := r.be.Warmup(ctx, handles)
 	return &WarmupJob{
-		repo:             repo,
+		repo:             r,
 		handlesWarmingUp: handlesWarmingUp,
 	}, err
 }


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 4da17a047..647cb5de7 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -13,7 +13,7 @@ permissions:
   contents: read
 
 env:
-  latest_go: "1.24.x"
+  latest_go: "1.23.x"
   GO111MODULE: on
 
 jobs:
@@ -23,29 +23,29 @@ jobs:
         # list of jobs to run:
         include:
           - job_name: Windows
-            go: 1.24.x
+            go: 1.23.x
             os: windows-latest
 
           - job_name: macOS
-            go: 1.24.x
+            go: 1.23.x
             os: macOS-latest
             test_fuse: false
 
           - job_name: Linux
-            go: 1.24.x
+            go: 1.23.x
             os: ubuntu-latest
             test_cloud_backends: true
             test_fuse: true
             check_changelog: true
 
           - job_name: Linux (race)
-            go: 1.24.x
+            go: 1.23.x
             os: ubuntu-latest
             test_fuse: true
             test_opts: "-race"
 
           - job_name: Linux
-            go: 1.23.x
+            go: 1.22.x
             os: ubuntu-latest
             test_fuse: true
 
@@ -185,7 +185,7 @@ jobs:
           # prepare credentials for Google Cloud Storage tests in a temp file
           export GOOGLE_APPLICATION_CREDENTIALS=$(mktemp --tmpdir restic-gcs-auth-XXXXXXX)
           echo $RESTIC_TEST_GS_APPLICATION_CREDENTIALS_B64 | base64 -d > $GOOGLE_APPLICATION_CREDENTIALS
-          go test -cover -parallel 5 -timeout 15m ./internal/backend/...
+          go test -cover -parallel 4 ./internal/backend/...
 
         # only run cloud backend tests for pull requests from and pushes to our
         # own repo, otherwise the secrets are not available
@@ -204,6 +204,7 @@ jobs:
 
   cross_compile:
     strategy:
+
       matrix:
         # run cross-compile in three batches parallel so the overall tests run faster
         subset:
@@ -253,7 +254,7 @@ jobs:
         uses: golangci/golangci-lint-action@v6
         with:
           # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
-          version: v1.64.8
+          version: v1.63.4
           args: --verbose --timeout 5m
 
         # only run golangci-lint for pull requests, otherwise ALL hints get
diff --git a/cmd/restic/cmd_repair_index_integration_test.go b/cmd/restic/cmd_repair_index_integration_test.go
index 9bfc93b40..2b76bf4b3 100644
--- a/cmd/restic/cmd_repair_index_integration_test.go
+++ b/cmd/restic/cmd_repair_index_integration_test.go
@@ -64,11 +64,11 @@ func TestRebuildIndex(t *testing.T) {
 }
 
 func TestRebuildIndexAlwaysFull(t *testing.T) {
-	indexFull := index.IndexFull
+	indexFull := index.Full
 	defer func() {
-		index.IndexFull = indexFull
+		index.Full = indexFull
 	}()
-	index.IndexFull = func(*index.Index) bool { return true }
+	index.Full = func(*index.Index) bool { return true }
 	testRebuildIndex(t, nil)
 }
 
diff --git a/internal/backend/s3/s3_test.go b/internal/backend/s3/s3_test.go
index 40f7cb0b3..470088e07 100644
--- a/internal/backend/s3/s3_test.go
+++ b/internal/backend/s3/s3_test.go
@@ -118,7 +118,7 @@ func newMinioTestSuite(t testing.TB) (*test.Suite[s3.Config], func()) {
 			},
 
 			Factory: location.NewHTTPBackendFactory("s3", s3.ParseConfig, location.NoPassword, func(ctx context.Context, cfg s3.Config, rt http.RoundTripper) (be backend.Backend, err error) {
-				for i := 0; i < 50; i++ {
+				for i := 0; i < 10; i++ {
 					be, err = s3.Create(ctx, cfg, rt)
 					if err != nil {
 						t.Logf("s3 open: try %d: error %v", i, err)
diff --git a/internal/backend/test/tests.go b/internal/backend/test/tests.go
index b3deba8da..f9dc0a17e 100644
--- a/internal/backend/test/tests.go
+++ b/internal/backend/test/tests.go
@@ -10,13 +10,11 @@ import (
 	"os"
 	"reflect"
 	"sort"
-	"sync"
 	"testing"
 	"time"
 
 	"github.com/restic/restic/internal/errors"
 	"github.com/restic/restic/internal/restic"
-	"golang.org/x/sync/errgroup"
 
 	"github.com/restic/restic/internal/test"
 
@@ -278,26 +276,16 @@ func (s *Suite[C]) TestList(t *testing.T) {
 	}
 
 	list1 := make(map[restic.ID]int64)
-	var m sync.Mutex
 
-	wg, ctx := errgroup.WithContext(context.TODO())
 	for i := 0; i < numTestFiles; i++ {
 		data := test.Random(random.Int(), random.Intn(100)+55)
-		wg.Go(func() error {
-			id := restic.Hash(data)
-			h := backend.Handle{Type: backend.PackFile, Name: id.String()}
-			err := b.Save(ctx, h, backend.NewByteReader(data, b.Hasher()))
-
-			m.Lock()
-			defer m.Unlock()
-			list1[id] = int64(len(data))
-			return err
-		})
-	}
-
-	err = wg.Wait()
-	if err != nil {
-		t.Fatal(err)
+		id := restic.Hash(data)
+		h := backend.Handle{Type: backend.PackFile, Name: id.String()}
+		err := b.Save(context.TODO(), h, backend.NewByteReader(data, b.Hasher()))
+		if err != nil {
+			t.Fatal(err)
+		}
+		list1[id] = int64(len(data))
 	}
 
 	t.Logf("wrote %v files", len(list1))
@@ -725,23 +713,18 @@ func (s *Suite[C]) delayedRemove(t testing.TB, be backend.Backend, handles ...ba
 	// Some backend (swift, I'm looking at you) may implement delayed
 	// removal of data. Let's wait a bit if this happens.
 
-	wg, ctx := errgroup.WithContext(context.TODO())
 	for _, h := range handles {
-		wg.Go(func() error {
-			err := be.Remove(ctx, h)
-			if s.ErrorHandler != nil {
-				err = s.ErrorHandler(t, be, err)
-			}
+		err := be.Remove(context.TODO(), h)
+		if s.ErrorHandler != nil {
+			err = s.ErrorHandler(t, be, err)
+		}
+		if err != nil {
 			return err
-		})
-	}
-	err := wg.Wait()
-	if err != nil {
-		return err
+		}
 	}
 
-	start := time.Now()
 	for _, h := range handles {
+		start := time.Now()
 		attempt := 0
 		var found bool
 		var err error
@@ -794,124 +777,125 @@ func delayedList(t testing.TB, b backend.Backend, tpe backend.FileType, max int,
 
 // TestBackend tests all functions of the backend.
 func (s *Suite[C]) TestBackend(t *testing.T) {
+	b := s.open(t)
+	defer s.close(t, b)
+
+	test.Assert(t, !b.IsNotExist(nil), "IsNotExist() recognized nil error")
+	test.Assert(t, !b.IsPermanentError(nil), "IsPermanentError() recognized nil error")
+
 	for _, tpe := range []backend.FileType{
 		backend.PackFile, backend.KeyFile, backend.LockFile,
 		backend.SnapshotFile, backend.IndexFile,
 	} {
-		t.Run(tpe.String(), func(t *testing.T) {
-			t.Parallel()
-
-			b := s.open(t)
-			defer s.close(t, b)
-
-			test.Assert(t, !b.IsNotExist(nil), "IsNotExist() recognized nil error")
-			test.Assert(t, !b.IsPermanentError(nil), "IsPermanentError() recognized nil error")
-
-			// detect non-existing files
-			for _, ts := range testStrings {
-				id, err := restic.ParseID(ts.id)
-				test.OK(t, err)
-
-				// test if blob is already in repository
-				h := backend.Handle{Type: tpe, Name: id.String()}
-				ret, err := beTest(context.TODO(), b, h)
-				test.OK(t, err)
-				test.Assert(t, !ret, "id %q was found (but should not have)", ts.id)
-
-				// try to stat a not existing blob
-				_, err = b.Stat(context.TODO(), h)
-				test.Assert(t, err != nil, "blob data could be extracted before creation")
-				test.Assert(t, b.IsNotExist(err), "IsNotExist() did not recognize Stat() error: %v", err)
-				test.Assert(t, b.IsPermanentError(err), "IsPermanentError() did not recognize Stat() error: %v", err)
-
-				// try to read not existing blob
-				err = testLoad(b, h)
-				test.Assert(t, err != nil, "blob could be read before creation")
-				test.Assert(t, b.IsNotExist(err), "IsNotExist() did not recognize Load() error: %v", err)
-				test.Assert(t, b.IsPermanentError(err), "IsPermanentError() did not recognize Load() error: %v", err)
-			}
+		// detect non-existing files
+		for _, ts := range testStrings {
+			id, err := restic.ParseID(ts.id)
+			test.OK(t, err)
 
-			// add files
-			for _, ts := range testStrings {
-				store(t, b, tpe, []byte(ts.data))
-
-				// test Load()
-				h := backend.Handle{Type: tpe, Name: ts.id}
-				buf, err := LoadAll(context.TODO(), b, h)
-				test.OK(t, err)
-				test.Equals(t, ts.data, string(buf))
-
-				// try to read it out with an offset and a length
-				start := 1
-				end := len(ts.data) - 2
-				length := end - start
-
-				buf2 := make([]byte, length)
-				var n int
-				err = b.Load(context.TODO(), h, len(buf2), int64(start), func(rd io.Reader) (ierr error) {
-					n, ierr = io.ReadFull(rd, buf2)
-					return ierr
-				})
-				test.OK(t, err)
-				test.OK(t, err)
-				test.Equals(t, len(buf2), n)
-				test.Equals(t, ts.data[start:end], string(buf2))
-			}
+			// test if blob is already in repository
+			h := backend.Handle{Type: tpe, Name: id.String()}
+			ret, err := beTest(context.TODO(), b, h)
+			test.OK(t, err)
+			test.Assert(t, !ret, "blob was found to exist before creating")
+
+			// try to stat a not existing blob
+			_, err = b.Stat(context.TODO(), h)
+			test.Assert(t, err != nil, "blob data could be extracted before creation")
+			test.Assert(t, b.IsNotExist(err), "IsNotExist() did not recognize Stat() error: %v", err)
+			test.Assert(t, b.IsPermanentError(err), "IsPermanentError() did not recognize Stat() error: %v", err)
+
+			// try to read not existing blob
+			err = testLoad(b, h)
+			test.Assert(t, err != nil, "blob could be read before creation")
+			test.Assert(t, b.IsNotExist(err), "IsNotExist() did not recognize Load() error: %v", err)
+			test.Assert(t, b.IsPermanentError(err), "IsPermanentError() did not recognize Load() error: %v", err)
+
+			// try to get string out, should fail
+			ret, err = beTest(context.TODO(), b, h)
+			test.OK(t, err)
+			test.Assert(t, !ret, "id %q was found (but should not have)", ts.id)
+		}
 
-			// test adding the first file again
-			ts := testStrings[0]
-			h := backend.Handle{Type: tpe, Name: ts.id}
+		// add files
+		for _, ts := range testStrings {
+			store(t, b, tpe, []byte(ts.data))
 
-			// remove and recreate
-			err := s.delayedRemove(t, b, h)
+			// test Load()
+			h := backend.Handle{Type: tpe, Name: ts.id}
+			buf, err := LoadAll(context.TODO(), b, h)
 			test.OK(t, err)
-
-			// test that the blob is gone
-			ok, err := beTest(context.TODO(), b, h)
+			test.Equals(t, ts.data, string(buf))
+
+			// try to read it out with an offset and a length
+			start := 1
+			end := len(ts.data) - 2
+			length := end - start
+
+			buf2 := make([]byte, length)
+			var n int
+			err = b.Load(context.TODO(), h, len(buf2), int64(start), func(rd io.Reader) (ierr error) {
+				n, ierr = io.ReadFull(rd, buf2)
+				return ierr
+			})
 			test.OK(t, err)
-			test.Assert(t, !ok, "removed blob still present")
-
-			// create blob
-			err = b.Save(context.TODO(), h, backend.NewByteReader([]byte(ts.data), b.Hasher()))
 			test.OK(t, err)
+			test.Equals(t, len(buf2), n)
+			test.Equals(t, ts.data[start:end], string(buf2))
+		}
 
-			// list items
-			IDs := restic.IDs{}
+		// test adding the first file again
+		ts := testStrings[0]
+		h := backend.Handle{Type: tpe, Name: ts.id}
 
-			for _, ts := range testStrings {
-				id, err := restic.ParseID(ts.id)
-				test.OK(t, err)
-				IDs = append(IDs, id)
-			}
+		// remove and recreate
+		err := s.delayedRemove(t, b, h)
+		test.OK(t, err)
 
-			list := delayedList(t, b, tpe, len(IDs), s.WaitForDelayedRemoval)
-			if len(IDs) != len(list) {
-				t.Fatalf("wrong number of IDs returned: want %d, got %d", len(IDs), len(list))
-			}
+		// test that the blob is gone
+		ok, err := beTest(context.TODO(), b, h)
+		test.OK(t, err)
+		test.Assert(t, !ok, "removed blob still present")
 
-			sort.Sort(IDs)
-			sort.Sort(list)
+		// create blob
+		err = b.Save(context.TODO(), h, backend.NewByteReader([]byte(ts.data), b.Hasher()))
+		test.OK(t, err)
 
-			if !reflect.DeepEqual(IDs, list) {
-				t.Fatalf("lists aren't equal, want:\n  %v\n  got:\n%v\n", IDs, list)
-			}
+		// list items
+		IDs := restic.IDs{}
 
-			var handles []backend.Handle
-			for _, ts := range testStrings {
-				id, err := restic.ParseID(ts.id)
-				test.OK(t, err)
+		for _, ts := range testStrings {
+			id, err := restic.ParseID(ts.id)
+			test.OK(t, err)
+			IDs = append(IDs, id)
+		}
 
-				h := backend.Handle{Type: tpe, Name: id.String()}
+		list := delayedList(t, b, tpe, len(IDs), s.WaitForDelayedRemoval)
+		if len(IDs) != len(list) {
+			t.Fatalf("wrong number of IDs returned: want %d, got %d", len(IDs), len(list))
+		}
 
-				found, err := beTest(context.TODO(), b, h)
-				test.OK(t, err)
-				test.Assert(t, found, fmt.Sprintf("id %v/%q not found", tpe, id))
+		sort.Sort(IDs)
+		sort.Sort(list)
 
-				handles = append(handles, h)
-			}
+		if !reflect.DeepEqual(IDs, list) {
+			t.Fatalf("lists aren't equal, want:\n  %v\n  got:\n%v\n", IDs, list)
+		}
 
-			test.OK(t, s.delayedRemove(t, b, handles...))
-		})
+		var handles []backend.Handle
+		for _, ts := range testStrings {
+			id, err := restic.ParseID(ts.id)
+			test.OK(t, err)
+
+			h := backend.Handle{Type: tpe, Name: id.String()}
+
+			found, err := beTest(context.TODO(), b, h)
+			test.OK(t, err)
+			test.Assert(t, found, fmt.Sprintf("id %v/%q not found", tpe, id))
+
+			handles = append(handles, h)
+		}
+
+		test.OK(t, s.delayedRemove(t, b, handles...))
 	}
 }
 
diff --git a/internal/repository/index/index_internal_test.go b/internal/repository/index/index_internal_test.go
index f55c9f546..67591ef8a 100644
--- a/internal/repository/index/index_internal_test.go
+++ b/internal/repository/index/index_internal_test.go
@@ -24,7 +24,7 @@ func TestIndexOversized(t *testing.T) {
 		})
 	}
 
-	rtest.Assert(t, !IndexOversized(idx), "index should not be considered oversized")
+	rtest.Assert(t, !Oversized(idx), "index should not be considered oversized")
 
 	// Add one more blob to exceed the limit
 	idx.store(packID, restic.Blob{
@@ -36,5 +36,5 @@ func TestIndexOversized(t *testing.T) {
 		Offset: uint(indexMaxBlobs+pack.MaxHeaderEntries) * 100,
 	})
 
-	rtest.Assert(t, IndexOversized(idx), "index should be considered oversized")
+	rtest.Assert(t, Oversized(idx), "index should be considered oversized")
 }
diff --git a/internal/repository/index/master_index_test.go b/internal/repository/index/master_index_test.go
index ac7beb84f..063af862d 100644
--- a/internal/repository/index/master_index_test.go
+++ b/internal/repository/index/master_index_test.go
@@ -466,16 +466,16 @@ func TestRewriteOversizedIndex(t *testing.T) {
 	const fullIndexCount = 1000
 
 	// replace index size checks for testing
-	originalIndexFull := index.IndexFull
-	originalIndexOversized := index.IndexOversized
+	originalIndexFull := index.Full
+	originalIndexOversized := index.Oversized
 	defer func() {
-		index.IndexFull = originalIndexFull
-		index.IndexOversized = originalIndexOversized
+		index.Full = originalIndexFull
+		index.Oversized = originalIndexOversized
 	}()
-	index.IndexFull = func(idx *index.Index) bool {
+	index.Full = func(idx *index.Index) bool {
 		return idx.Len(restic.DataBlob) > fullIndexCount
 	}
-	index.IndexOversized = func(idx *index.Index) bool {
+	index.Oversized = func(idx *index.Index) bool {
 		return idx.Len(restic.DataBlob) > 2*fullIndexCount
 	}
 
diff --git a/internal/repository/repository_test.go b/internal/repository/repository_test.go
index 1b0d47c8f..0ce1eab14 100644
--- a/internal/repository/repository_test.go
+++ b/internal/repository/repository_test.go
@@ -333,7 +333,7 @@ func TestRepositoryIncrementalIndex(t *testing.T) {
 func testRepositoryIncrementalIndex(t *testing.T, version uint) {
 	repo, _, _ := repository.TestRepositoryWithVersion(t, version)
 
-	index.IndexFull = func(*index.Index) bool { return true }
+	index.Full = func(*index.Index) bool { return true }
 
 	// add a few rounds of packs
 	for j := 0; j < 5; j++ {