Repository: restic/restic
Pull request #4743 (2024-03-29 13:00:05)

Deprecate s3legacy layout
<!--
Thank you very much for contributing code or documentation to restic! Please
fill out the following questions to make it easier for us to review your
changes.
-->

What does this PR change? What problem does it solve?
-----------------------------------------------------
Disable support for the s3legacy layout to allow us to finally remove the code in a future restic version.

The local and sftp backend were also able to read repositories using the s3legacy layout. There is currently no documented migration path for such repositories, however, it is completely unclear whether such repositories even exist. I did not implement an upgrade path in restic, as the layout upgrade can also be handled using a short bash script if necessary.

<!--
Describe the changes and their purpose here, as detailed as needed.
-->

Was the change previously discussed in an issue or on the forum?
----------------------------------------------------------------
Fixes https://github.com/restic/restic/issues/4602
<!--
Link issues and relevant forum posts here.

If this PR resolves an issue on GitHub, use "Closes #1234" so that the issue
is closed automatically when this PR is merged.
-->

Checklist
---------

<!--
You do not need to check all the boxes below all at once. Feel free to take
your time and add more commits. If you're done and ready for review, please
check the last box. Enable a checkbox by replacing [ ] with [x].
-->

- [x] I have read the [contribution guidelines](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#providing-patches).
- [x] I have [enabled 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).
- [ ] I have added tests for all code changes.
- [ ] I have added documentation for relevant changes (in the manual).
- [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)).
- [x] I have run `gofmt` on the code in all commits.
- [x] All commit messages are formatted in the same style as [the other commits in the repo](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#git-commits).
- [x] I'm done! This pull request is ready for review.



Patch:
diff --git a/.golangci.yml b/.golangci.yml
index e632965bb..7dc6a8e7f 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -38,8 +38,6 @@ linters:
     # ensure that http response bodies are closed
     - bodyclose
 
-    - importas
-
 issues:
   # don't use the default exclude rules, this hides (among others) ignored
   # errors from Close() calls
@@ -60,10 +58,4 @@ issues:
   exclude-rules:
     # revive: ignore unused parameters in tests
     - path: (_test\.go|testing\.go|backend/.*/tests\.go)
-      text: "unused-parameter:"
-
-linters-settings:
-  importas:
-    alias:
-      - pkg: github.com/restic/restic/internal/test
-        alias: rtest
+      text: "unused-parameter:"
\ No newline at end of file
diff --git a/changelog/unreleased/issue-3600 b/changelog/unreleased/issue-3600
deleted file mode 100644
index 0da66d382..000000000
--- a/changelog/unreleased/issue-3600
+++ /dev/null
@@ -1,11 +0,0 @@
-Bugfix: `backup` works if xattrs above the backup target cannot be read
-
-When backup targets are specified using absolute paths, then `backup` also
-includes information about the parent folders of the backup targets in the
-snapshot. If the extended attributes for some of these folders could not be
-read due to missing permissions, this caused the backup to fail. This has been
-fixed.
-
-https://github.com/restic/restic/issues/3600
-https://github.com/restic/restic/pull/4668
-https://forum.restic.net/t/parent-directories-above-the-snapshot-source-path-fatal-error-permission-denied/7216
diff --git a/changelog/unreleased/issue-4287 b/changelog/unreleased/issue-4287
deleted file mode 100644
index df4fc5590..000000000
--- a/changelog/unreleased/issue-4287
+++ /dev/null
@@ -1,14 +0,0 @@
-Enhancement: support connection to rest-server using unix socket
-
-Restic now supports connecting to rest-server using a unix socket for
-rest-server version 0.13.0 or later.
-
-This allows running restic as follows:
-
-```
-rest-server --listen unix:/tmp/rest.socket --data /path/to/data &
-restic -r rest:http+unix:///tmp/rest.socket:/my_backup_repo/ [...]
-```
-
-https://github.com/restic/restic/issues/4287
-https://github.com/restic/restic/pull/4655
diff --git a/changelog/unreleased/issue-4602 b/changelog/unreleased/issue-4602
index 3cba63876..7532bcb1e 100644
--- a/changelog/unreleased/issue-4602
+++ b/changelog/unreleased/issue-4602
@@ -1,7 +1,7 @@
-Change: Deprecate legacy index format
+Change: Deprecate legacy index format and s3legacy layout
 
 Support for the legacy index format used by restic before version 0.2.0 has
-been depreacted and will be removed in the next minor restic version. You can
+been deprecated and will be removed in the next minor restic version. You can
 use `restic repair index` to update the index to the current format.
 
 It is possible to temporarily reenable support for the legacy index format by
@@ -9,5 +9,15 @@ setting the environment variable
 `RESTIC_FEATURES=deprecate-legacy-index=false`. Note that this feature flag
 will be removed in the next minor restic version.
 
+Support for the s3legacy layout used for the S3 backend before restic 0.7.0
+has been deprecated and will be removed in the next minor restic version. You
+can migrate your S3 repository using `RESTIC_FEATURES=deprecate-s3-legacy-layout=false restic migrate s3_layout`.
+
+It is possible to temporarily reenable support for the legacy s3layout by
+setting the environment variable
+`RESTIC_FEATURES=deprecate-s3-legacy-layout=false`. Note that this feature flag
+will be removed in the next minor restic version.
+
 https://github.com/restic/restic/issues/4602
 https://github.com/restic/restic/pull/4724
+https://github.com/restic/restic/pull/4743
diff --git a/changelog/unreleased/issue-4744 b/changelog/unreleased/issue-4744
deleted file mode 100644
index b0ede1c5c..000000000
--- a/changelog/unreleased/issue-4744
+++ /dev/null
@@ -1,9 +0,0 @@
-Change: Include full key ID in JSON output of `key list`
-
-We have changed the JSON output of the `key list` command to include the full
-key ID instead of just a shortened version, as the latter can be ambiguous
-in some rare cases. To derive the short ID, please truncate the full ID down to
-eight characters.
-
-https://github.com/restic/restic/issues/4744
-https://github.com/restic/restic/pull/4745
diff --git a/changelog/unreleased/pull-4737 b/changelog/unreleased/pull-4737
deleted file mode 100644
index 2637c8f83..000000000
--- a/changelog/unreleased/pull-4737
+++ /dev/null
@@ -1,5 +0,0 @@
-Enhancement: include snapshot id in reason field of forget JSON output
-
-The JSON output of the `forget` command now includes the `id` and `short_id` of a snapshot in the `reason` field.
-
-https://github.com/restic/restic/pull/4737
diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go
index d634576c0..f2fc1da8c 100644
--- a/cmd/restic/cmd_forget.go
+++ b/cmd/restic/cmd_forget.go
@@ -245,16 +245,16 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
 					PrintSnapshots(globalOptions.stdout, keep, reasons, opts.Compact)
 					Printf("\n")
 				}
-				fg.Keep = asJSONSnapshots(keep)
+				addJSONSnapshots(&fg.Keep, keep)
 
 				if len(remove) != 0 && !gopts.Quiet && !gopts.JSON {
 					Printf("remove %d snapshots:\n", len(remove))
 					PrintSnapshots(globalOptions.stdout, remove, nil, opts.Compact)
 					Printf("\n")
 				}
-				fg.Remove = asJSONSnapshots(remove)
+				addJSONSnapshots(&fg.Remove, remove)
 
-				fg.Reasons = asJSONKeeps(reasons)
+				fg.Reasons = reasons
 
 				jsonGroups = append(jsonGroups, &fg)
 
@@ -302,47 +302,23 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
 
 // ForgetGroup helps to print what is forgotten in JSON.
 type ForgetGroup struct {
-	Tags    []string     `json:"tags"`
-	Host    string       `json:"host"`
-	Paths   []string     `json:"paths"`
-	Keep    []Snapshot   `json:"keep"`
-	Remove  []Snapshot   `json:"remove"`
-	Reasons []KeepReason `json:"reasons"`
+	Tags    []string            `json:"tags"`
+	Host    string              `json:"host"`
+	Paths   []string            `json:"paths"`
+	Keep    []Snapshot          `json:"keep"`
+	Remove  []Snapshot          `json:"remove"`
+	Reasons []restic.KeepReason `json:"reasons"`
 }
 
-func asJSONSnapshots(list restic.Snapshots) []Snapshot {
-	var resultList []Snapshot
+func addJSONSnapshots(js *[]Snapshot, list restic.Snapshots) {
 	for _, sn := range list {
 		k := Snapshot{
 			Snapshot: sn,
 			ID:       sn.ID(),
 			ShortID:  sn.ID().Str(),
 		}
-		resultList = append(resultList, k)
+		*js = append(*js, k)
 	}
-	return resultList
-}
-
-// KeepReason helps to print KeepReasons as JSON with Snapshots with their ID included.
-type KeepReason struct {
-	Snapshot Snapshot `json:"snapshot"`
-	Matches  []string `json:"matches"`
-}
-
-func asJSONKeeps(list []restic.KeepReason) []KeepReason {
-	var resultList []KeepReason
-	for _, keep := range list {
-		k := KeepReason{
-			Snapshot: Snapshot{
-				Snapshot: keep.Snapshot,
-				ID:       keep.Snapshot.ID(),
-				ShortID:  keep.Snapshot.ID().Str(),
-			},
-			Matches: keep.Matches,
-		}
-		resultList = append(resultList, k)
-	}
-	return resultList
 }
 
 func printJSONForget(stdout io.Writer, forgets []*ForgetGroup) error {
diff --git a/cmd/restic/cmd_key_list.go b/cmd/restic/cmd_key_list.go
index fcca6055a..9bddb5ed3 100644
--- a/cmd/restic/cmd_key_list.go
+++ b/cmd/restic/cmd_key_list.go
@@ -53,7 +53,6 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions
 	type keyInfo struct {
 		Current  bool   `json:"current"`
 		ID       string `json:"id"`
-		ShortID  string `json:"-"`
 		UserName string `json:"userName"`
 		HostName string `json:"hostName"`
 		Created  string `json:"created"`
@@ -71,8 +70,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions
 
 		key := keyInfo{
 			Current:  id == s.KeyID(),
-			ID:       id.String(),
-			ShortID:  id.Str(),
+			ID:       id.Str(),
 			UserName: k.Username,
 			HostName: k.Hostname,
 			Created:  k.Created.Local().Format(TimeFormat),
@@ -93,7 +91,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions
 	}
 
 	tab := table.New()
-	tab.AddColumn(" ID", "{{if .Current}}*{{else}} {{end}}{{ .ShortID }}")
+	tab.AddColumn(" ID", "{{if .Current}}*{{else}} {{end}}{{ .ID }}")
 	tab.AddColumn("User", "{{ .UserName }}")
 	tab.AddColumn("Host", "{{ .HostName }}")
 	tab.AddColumn("Created", "{{ .Created }}")
diff --git a/doc/030_preparing_a_new_repo.rst b/doc/030_preparing_a_new_repo.rst
index 0c50b65be..8661f5904 100644
--- a/doc/030_preparing_a_new_repo.rst
+++ b/doc/030_preparing_a_new_repo.rst
@@ -201,16 +201,15 @@ scheme like this:
     $ restic -r rest:http://host:8000/ init
 
 Depending on your REST server setup, you can use HTTPS protocol,
-unix socket, password protection, multiple repositories or any
-combination of those features. The TCP/IP port is also configurable.
-Here are some more examples:
+password protection, multiple repositories or any combination of
+those features. The TCP/IP port is also configurable. Here
+are some more examples:
 
 .. code-block:: console
 
     $ restic -r rest:https://host:8000/ init
     $ restic -r rest:https://user:pass@host:8000/ init
     $ restic -r rest:https://user:pass@host:8000/my_backup_repo/ init
-    $ restic -r rest:http+unix:///tmp/rest.socket:/my_backup_repo/ init
 
 The server username and password can be specified using environment
 variables as well:
diff --git a/doc/047_tuning_backup_parameters.rst b/doc/047_tuning_backup_parameters.rst
index a6b998cfe..8456693e7 100644
--- a/doc/047_tuning_backup_parameters.rst
+++ b/doc/047_tuning_backup_parameters.rst
@@ -121,7 +121,7 @@ Feature flags allow disabling or enabling certain experimental restic features.
 can be specified via the ``RESTIC_FEATURES`` environment variable. The variable expects a
 comma-separated list of ``key[=value],key2[=value2]`` pairs. The key is the name of a feature
 flag. The value is optional and can contain either the value ``true`` (default if omitted)
-or ``false``. The list of currently available feature flags is shown by the ``features``
+or ``false``. The list of currently available feautre flags is shown by the ``features``
 command.
 
 Restic will return an error if an invalid feature flag is specified. No longer relevant
diff --git a/doc/075_scripting.rst b/doc/075_scripting.rst
index 28419c292..d51516cbe 100644
--- a/doc/075_scripting.rst
+++ b/doc/075_scripting.rst
@@ -367,13 +367,13 @@ Snapshot object
 
 Reason object
 
-+----------------+-----------------------------------------------------------+
-| ``snapshot``   | Snapshot object, including ``id`` and ``short_id`` fields |
-+----------------+-----------------------------------------------------------+
-| ``matches``    | Array containing descriptions of the matching criteria    |
-+----------------+-----------------------------------------------------------+
-| ``counters``   | Object containing counters used by the policies           |
-+----------------+-----------------------------------------------------------+
++----------------+---------------------------------------------------------+
+| ``snapshot``   | Snapshot object, without ``id`` and ``short_id`` fields |
++----------------+---------------------------------------------------------+
+| ``matches``    | Array containing descriptions of the matching criteria  |
++----------------+---------------------------------------------------------+
+| ``counters``   | Object containing counters used by the policies         |
++----------------+---------------------------------------------------------+
 
 
 init
diff --git a/go.mod b/go.mod
index c928b4a97..6e546974e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,8 @@
 module github.com/restic/restic
 
 require (
-	cloud.google.com/go/storage v1.40.0
-	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0
+	cloud.google.com/go/storage v1.39.0
+	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1
 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1
 	github.com/Backblaze/blazer v0.6.1
@@ -17,7 +17,6 @@ require (
 	github.com/minio/minio-go/v7 v7.0.66
 	github.com/minio/sha256-simd v1.0.1
 	github.com/ncw/swift/v2 v2.0.2
-	github.com/peterbourgon/unixtransport v0.0.4
 	github.com/pkg/errors v0.9.1
 	github.com/pkg/profile v1.7.0
 	github.com/pkg/sftp v1.13.6
@@ -26,22 +25,22 @@ require (
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5
 	go.uber.org/automaxprocs v1.5.3
-	golang.org/x/crypto v0.21.0
-	golang.org/x/net v0.23.0
-	golang.org/x/oauth2 v0.18.0
+	golang.org/x/crypto v0.19.0
+	golang.org/x/net v0.21.0
+	golang.org/x/oauth2 v0.17.0
 	golang.org/x/sync v0.6.0
-	golang.org/x/sys v0.18.0
-	golang.org/x/term v0.18.0
+	golang.org/x/sys v0.17.0
+	golang.org/x/term v0.17.0
 	golang.org/x/text v0.14.0
 	golang.org/x/time v0.5.0
-	google.golang.org/api v0.170.0
+	google.golang.org/api v0.166.0
 )
 
 require (
-	cloud.google.com/go v0.112.1 // indirect
+	cloud.google.com/go v0.112.0 // indirect
 	cloud.google.com/go/compute v1.24.0 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
-	cloud.google.com/go/iam v1.1.7 // indirect
+	cloud.google.com/go/iam v1.1.6 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
 	github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
@@ -52,12 +51,12 @@ require (
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
-	github.com/golang/protobuf v1.5.4 // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
 	github.com/google/s2a-go v0.1.7 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
-	github.com/googleapis/gax-go/v2 v2.12.3 // indirect
+	github.com/googleapis/gax-go/v2 v2.12.1 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.6 // indirect
@@ -72,17 +71,17 @@ require (
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	go.opencensus.io v0.24.0 // indirect
-	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
-	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
-	go.opentelemetry.io/otel v1.24.0 // indirect
-	go.opentelemetry.io/otel/metric v1.24.0 // indirect
-	go.opentelemetry.io/otel/trace v1.24.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect
+	go.opentelemetry.io/otel v1.23.0 // indirect
+	go.opentelemetry.io/otel/metric v1.23.0 // indirect
+	go.opentelemetry.io/otel/trace v1.23.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 // indirect
-	google.golang.org/grpc v1.62.1 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
+	google.golang.org/grpc v1.61.1 // indirect
+	google.golang.org/protobuf v1.32.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 1a7dc1a79..668d6a339 100644
--- a/go.sum
+++ b/go.sum
@@ -1,16 +1,16 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
-cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
+cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
+cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
 cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
 cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
 cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
 cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
-cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM=
-cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA=
-cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw=
-cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM=
+cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
+cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
+cloud.google.com/go/storage v1.39.0 h1:brbjUa4hbDHhpQf48tjqMaXEV+f1OGoaTmQau9tmCsA=
+cloud.google.com/go/storage v1.39.0/go.mod h1:OAEj/WZwUYjA3YHQ10/YcN9ttGuEpLwvaoyBXIPikEk=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 h1:c4k2FIYIh4xtwqrQwV0Ct1v5+ehlNXj5NI/MWVsiTkQ=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2/go.mod h1:5FDJtLEO/GxwNgUxbwrY3LP0pEoThTQJtk2oysdXHxM=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
@@ -36,6 +36,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=
 github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -53,6 +54,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
 github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
 github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -82,8 +84,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
-github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -105,8 +107,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
 github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
-github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
-github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
+github.com/googleapis/gax-go/v2 v2.12.1 h1:9F8GV9r9ztXyAi00gsMQHNoF51xPZm8uj1dpYt2ZETM=
+github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc=
 github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
 github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
 github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
@@ -126,7 +128,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 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/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.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw=
@@ -140,11 +141,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/ncw/swift/v2 v2.0.2 h1:jx282pcAKFhmoZBSdMcCRFn9VWkoBIRsCpe+yZq7vEk=
 github.com/ncw/swift/v2 v2.0.2/go.mod h1:z0A9RVdYPjNjXVo2pDOPxZ4eu3oarO1P91fTItcb+Kg=
-github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
-github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/peterbourgon/ff/v3 v3.3.1/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
-github.com/peterbourgon/unixtransport v0.0.4 h1:UTF0FxXCAglvoZz9jaGPYjEg52DjBLDYGMJvJni6Tfw=
-github.com/peterbourgon/unixtransport v0.0.4/go.mod h1:o8aUkOCa8W/BIXpi15uKvbSabjtBh0JhSOJGSfoOhAU=
 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -188,17 +184,17 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
-go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
-go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
-go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
-go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
-go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
-go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
-go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA=
+go.opentelemetry.io/otel v1.23.0 h1:Df0pqjqExIywbMCMTxkAwzjLZtRf+bBKLbUcpxO2C9E=
+go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0=
+go.opentelemetry.io/otel/metric v1.23.0 h1:pazkx7ss4LFVVYSxYew7L5I6qvLXHA0Ap2pwV+9Cnpo=
+go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo=
+go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
+go.opentelemetry.io/otel/trace v1.23.0 h1:37Ik5Ib7xfYVb4V1UtnT97T1jI+AoIYkJyPkuL4iJgI=
+go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk=
 go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
 go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -206,15 +202,14 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -226,18 +221,16 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
-golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
+golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
+golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
 golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -253,16 +246,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -280,13 +271,12 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200423201157-2723c5de0d66/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
-google.golang.org/api v0.170.0 h1:zMaruDePM88zxZBG+NG8+reALO2rfLhe/JShitLyT48=
-google.golang.org/api v0.170.0/go.mod h1:/xql9M2btF85xac/VAm4PsLMTLVGUOpq4BE9R8jyNy8=
+google.golang.org/api v0.166.0 h1:6m4NUwrZYhAaVIHZWxaKjw1L1vNAjtMwORmKRyEEo24=
+google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
@@ -296,17 +286,17 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
 google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
-google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c h1:kaI7oewGK5YnVwj+Y+EJBO/YN1ht8iTL9XkFHtVZLsc=
-google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 h1:9IZDv+/GcI6u+a4jRFRLxQs0RUCfavGfoOgEW6jpkI0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
+google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c h1:9g7erC9qu44ks7UK4gDNlnk4kOxZG707xKm4jVniy6o=
+google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
-google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
+google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
+google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -318,14 +308,13 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
+google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go
index 146ff3a7c..050a0e2c7 100644
--- a/internal/archiver/archiver.go
+++ b/internal/archiver/archiver.go
@@ -237,8 +237,8 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
 }
 
 // nodeFromFileInfo returns the restic node from an os.FileInfo.
-func (arch *Archiver) nodeFromFileInfo(snPath, filename string, fi os.FileInfo, ignoreXattrListError bool) (*restic.Node, error) {
-	node, err := restic.NodeFromFileInfo(filename, fi, ignoreXattrListError)
+func (arch *Archiver) nodeFromFileInfo(snPath, filename string, fi os.FileInfo) (*restic.Node, error) {
+	node, err := restic.NodeFromFileInfo(filename, fi)
 	if !arch.WithAtime {
 		node.AccessTime = node.ModTime
 	}
@@ -289,7 +289,7 @@ func (arch *Archiver) wrapLoadTreeError(id restic.ID, err error) error {
 func (arch *Archiver) saveDir(ctx context.Context, snPath string, dir string, fi os.FileInfo, previous *restic.Tree, complete CompleteFunc) (d FutureNode, err error) {
 	debug.Log("%v %v", snPath, dir)
 
-	treeNode, err := arch.nodeFromFileInfo(snPath, dir, fi, false)
+	treeNode, err := arch.nodeFromFileInfo(snPath, dir, fi)
 	if err != nil {
 		return FutureNode{}, err
 	}
@@ -444,7 +444,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
 				debug.Log("%v hasn't changed, using old list of blobs", target)
 				arch.trackItem(snPath, previous, previous, ItemStats{}, time.Since(start))
 				arch.CompleteBlob(previous.Size)
-				node, err := arch.nodeFromFileInfo(snPath, target, fi, false)
+				node, err := arch.nodeFromFileInfo(snPath, target, fi)
 				if err != nil {
 					return FutureNode{}, false, err
 				}
@@ -540,7 +540,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
 	default:
 		debug.Log("  %v other", target)
 
-		node, err := arch.nodeFromFileInfo(snPath, target, fi, false)
+		node, err := arch.nodeFromFileInfo(snPath, target, fi)
 		if err != nil {
 			return FutureNode{}, false, err
 		}
@@ -623,9 +623,7 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *Tree,
 		}
 
 		debug.Log("%v, dir node data loaded from %v", snPath, atree.FileInfoPath)
-		// in some cases reading xattrs for directories above the backup target is not allowed
-		// thus ignore errors for such folders.
-		node, err = arch.nodeFromFileInfo(snPath, atree.FileInfoPath, fi, true)
+		node, err = arch.nodeFromFileInfo(snPath, atree.FileInfoPath, fi)
 		if err != nil {
 			return FutureNode{}, 0, err
 		}
diff --git a/internal/archiver/file_saver.go b/internal/archiver/file_saver.go
index d10334301..7f11bff8a 100644
--- a/internal/archiver/file_saver.go
+++ b/internal/archiver/file_saver.go
@@ -29,7 +29,7 @@ type FileSaver struct {
 
 	CompleteBlob func(bytes uint64)
 
-	NodeFromFileInfo func(snPath, filename string, fi os.FileInfo, ignoreXattrListError bool) (*restic.Node, error)
+	NodeFromFileInfo func(snPath, filename string, fi os.FileInfo) (*restic.Node, error)
 }
 
 // NewFileSaver returns a new file saver. A worker pool with fileWorkers is
@@ -156,7 +156,7 @@ func (s *FileSaver) saveFile(ctx context.Context, chnker *chunker.Chunker, snPat
 
 	debug.Log("%v", snPath)
 
-	node, err := s.NodeFromFileInfo(snPath, f.Name(), fi, false)
+	node, err := s.NodeFromFileInfo(snPath, f.Name(), fi)
 	if err != nil {
 		_ = f.Close()
 		completeError(err)
diff --git a/internal/backend/http_transport.go b/internal/backend/http_transport.go
index 19b20dc6a..9ee1c91f1 100644
--- a/internal/backend/http_transport.go
+++ b/internal/backend/http_transport.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/peterbourgon/unixtransport"
 	"github.com/restic/restic/internal/debug"
 	"github.com/restic/restic/internal/errors"
 )
@@ -83,8 +82,6 @@ func Transport(opts TransportOptions) (http.RoundTripper, error) {
 		TLSClientConfig:       &tls.Config{},
 	}
 
-	unixtransport.Register(tr)
-
 	if opts.InsecureTLS {
 		tr.TLSClientConfig.InsecureSkipVerify = true
 	}
diff --git a/internal/backend/layout/layout.go b/internal/backend/layout/layout.go
index b600566a4..052fd66ca 100644
--- a/internal/backend/layout/layout.go
+++ b/internal/backend/layout/layout.go
@@ -10,6 +10,7 @@ import (
 	"github.com/restic/restic/internal/backend"
 	"github.com/restic/restic/internal/debug"
 	"github.com/restic/restic/internal/errors"
+	"github.com/restic/restic/internal/feature"
 	"github.com/restic/restic/internal/fs"
 	"github.com/restic/restic/internal/restic"
 )
@@ -93,6 +94,8 @@ func hasBackendFile(ctx context.Context, fs Filesystem, dir string) (bool, error
 // cannot be detected automatically.
 var ErrLayoutDetectionFailed = errors.New("auto-detecting the filesystem layout failed")
 
+var ErrLegacyLayoutFound = errors.New("detected legacy S3 layout. Use `RESTIC_FEATURES=deprecate-s3-legacy-layout=false restic migrate s3_layout` to migrate your repository")
+
 // DetectLayout tries to find out which layout is used in a local (or sftp)
 // filesystem at the given path. If repo is nil, an instance of LocalFilesystem
 // is used.
@@ -123,6 +126,10 @@ func DetectLayout(ctx context.Context, repo Filesystem, dir string) (Layout, err
 	}
 
 	if foundKeyFile && !foundKeysFile {
+		if feature.Flag.Enabled(feature.DeprecateS3LegacyLayout) {
+			return nil, ErrLegacyLayoutFound
+		}
+
 		debug.Log("found s3 layout at %v", dir)
 		return &S3LegacyLayout{
 			Path: dir,
@@ -145,6 +152,10 @@ func ParseLayout(ctx context.Context, repo Filesystem, layout, defaultLayout, pa
 			Join: repo.Join,
 		}
 	case "s3legacy":
+		if feature.Flag.Enabled(feature.DeprecateS3LegacyLayout) {
+			return nil, ErrLegacyLayoutFound
+		}
+
 		l = &S3LegacyLayout{
 			Path: path,
 			Join: repo.Join,
diff --git a/internal/backend/local/config.go b/internal/backend/local/config.go
index dc5e7948c..e08f05550 100644
--- a/internal/backend/local/config.go
+++ b/internal/backend/local/config.go
@@ -10,7 +10,7 @@ import (
 // Config holds all information needed to open a local repository.
 type Config struct {
 	Path   string
-	Layout string `option:"layout" help:"use this backend directory layout (default: auto-detect)"`
+	Layout string `option:"layout" help:"use this backend directory layout (default: auto-detect) (deprecated)"`
 
 	Connections uint `option:"connections" help:"set a limit for the number of concurrent operations (default: 2)"`
 }
diff --git a/internal/backend/s3/config.go b/internal/backend/s3/config.go
index b4d44399f..4aea4c3d1 100644
--- a/internal/backend/s3/config.go
+++ b/internal/backend/s3/config.go
@@ -20,7 +20,7 @@ type Config struct {
 	Secret       options.SecretString
 	Bucket       string
 	Prefix       string
-	Layout       string `option:"layout" help:"use this backend layout (default: auto-detect)"`
+	Layout       string `option:"layout" help:"use this backend layout (default: auto-detect) (deprecated)"`
 	StorageClass string `option:"storage-class" help:"set S3 storage class (STANDARD, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING or REDUCED_REDUNDANCY)"`
 
 	Connections   uint   `option:"connections" help:"set a limit for the number of concurrent connections (default: 5)"`
diff --git a/internal/backend/sftp/config.go b/internal/backend/sftp/config.go
index 65af50d19..aa8ac7bff 100644
--- a/internal/backend/sftp/config.go
+++ b/internal/backend/sftp/config.go
@@ -13,7 +13,7 @@ import (
 type Config struct {
 	User, Host, Port, Path string
 
-	Layout  string `option:"layout"  help:"use this backend directory layout (default: auto-detect)"`
+	Layout  string `option:"layout"  help:"use this backend directory layout (default: auto-detect) (deprecated)"`
 	Command string `option:"command" help:"specify command to create sftp connection"`
 	Args    string `option:"args"    help:"specify arguments for ssh"`
 
diff --git a/internal/feature/registry.go b/internal/feature/registry.go
index 4693b8909..2d2e45edf 100644
--- a/internal/feature/registry.go
+++ b/internal/feature/registry.go
@@ -5,13 +5,15 @@ var Flag = New()
 
 // flag names are written in kebab-case
 const (
-	DeprecateLegacyIndex FlagName = "deprecate-legacy-index"
-	DeviceIDForHardlinks FlagName = "device-id-for-hardlinks"
+	DeprecateLegacyIndex    FlagName = "deprecate-legacy-index"
+	DeprecateS3LegacyLayout FlagName = "deprecate-s3-legacy-layout"
+	DeviceIDForHardlinks    FlagName = "device-id-for-hardlinks"
 )
 
 func init() {
 	Flag.SetFlags(map[FlagName]FlagDesc{
-		DeprecateLegacyIndex: {Type: Beta, Description: "disable support for index format used by restic 0.1.0. Use `restic repair index` to update the index if necessary."},
-		DeviceIDForHardlinks: {Type: Alpha, Description: "store deviceID only for hardlinks to reduce metadata changes for example when using btrfs subvolumes. Will be removed in a future restic version after repository format 3 is available"},
+		DeprecateLegacyIndex:    {Type: Beta, Description: "disable support for index format used by restic 0.1.0. Use `restic repair index` to update the index if necessary."},
+		DeprecateS3LegacyLayout: {Type: Beta, Description: "disable support for S3 legacy layout used up to restic 0.7.0. Use `RESTIC_FEATURES=deprecate-s3-legacy-layout=false restic migrate s3_layout` to migrate your S3 repository if necessary."},
+		DeviceIDForHardlinks:    {Type: Alpha, Description: "store deviceID only for hardlinks to reduce metadata changes for example when using btrfs subvolumes. Will be removed in a future restic version after repository format 3 is available"},
 	})
 }
diff --git a/internal/restic/node.go b/internal/restic/node.go
index 9613cf3c2..e7688aada 100644
--- a/internal/restic/node.go
+++ b/internal/restic/node.go
@@ -134,7 +134,7 @@ func (node Node) String() string {
 
 // NodeFromFileInfo returns a new node from the given path and FileInfo. It
 // returns the first error that is encountered, together with a node.
-func NodeFromFileInfo(path string, fi os.FileInfo, ignoreXattrListError bool) (*Node, error) {
+func NodeFromFileInfo(path string, fi os.FileInfo) (*Node, error) {
 	mask := os.ModePerm | os.ModeType | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
 	node := &Node{
 		Path:    path,
@@ -148,7 +148,7 @@ func NodeFromFileInfo(path string, fi os.FileInfo, ignoreXattrListError bool) (*
 		node.Size = uint64(fi.Size())
 	}
 
-	err := node.fillExtra(path, fi, ignoreXattrListError)
+	err := node.fillExtra(path, fi)
 	return node, err
 }
 
@@ -675,7 +675,7 @@ func lookupGroup(gid uint32) string {
 	return group
 }
 
-func (node *Node) fillExtra(path string, fi os.FileInfo, ignoreXattrListError bool) error {
+func (node *Node) fillExtra(path string, fi os.FileInfo) error {
 	stat, ok := toStatT(fi.Sys())
 	if !ok {
 		// fill minimal info with current values for uid, gid
@@ -719,7 +719,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo, ignoreXattrListError bo
 	allowExtended, err := node.fillGenericAttributes(path, fi, stat)
 	if allowExtended {
 		// Skip processing ExtendedAttributes if allowExtended is false.
-		errEx := node.fillExtendedAttributes(path, ignoreXattrListError)
+		errEx := node.fillExtendedAttributes(path)
 		if err == nil {
 			err = errEx
 		} else {
@@ -729,13 +729,10 @@ func (node *Node) fillExtra(path string, fi os.FileInfo, ignoreXattrListError bo
 	return err
 }
 
-func (node *Node) fillExtendedAttributes(path string, ignoreListError bool) error {
+func (node *Node) fillExtendedAttributes(path string) error {
 	xattrs, err := Listxattr(path)
 	debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err)
 	if err != nil {
-		if ignoreListError && IsListxattrPermissionError(err) {
-			return nil
-		}
 		return err
 	}
 
diff --git a/internal/restic/node_aix.go b/internal/restic/node_aix.go
index 8ee9022c9..def46bd60 100644
--- a/internal/restic/node_aix.go
+++ b/internal/restic/node_aix.go
@@ -33,10 +33,6 @@ func Listxattr(path string) ([]string, error) {
 	return nil, nil
 }
 
-func IsListxattrPermissionError(_ error) bool {
-	return false
-}
-
 // Setxattr is a no-op on AIX.
 func Setxattr(path, name string, data []byte) error {
 	return nil
diff --git a/internal/restic/node_netbsd.go b/internal/restic/node_netbsd.go
index cf1fa36bd..1a47299be 100644
--- a/internal/restic/node_netbsd.go
+++ b/internal/restic/node_netbsd.go
@@ -23,10 +23,6 @@ func Listxattr(path string) ([]string, error) {
 	return nil, nil
 }
 
-func IsListxattrPermissionError(_ error) bool {
-	return false
-}
-
 // Setxattr is a no-op on netbsd.
 func Setxattr(path, name string, data []byte) error {
 	return nil
diff --git a/internal/restic/node_openbsd.go b/internal/restic/node_openbsd.go
index 4f1c0dacb..e60eb9dc8 100644
--- a/internal/restic/node_openbsd.go
+++ b/internal/restic/node_openbsd.go
@@ -23,10 +23,6 @@ func Listxattr(path string) ([]string, error) {
 	return nil, nil
 }
 
-func IsListxattrPermissionError(_ error) bool {
-	return false
-}
-
 // Setxattr is a no-op on openbsd.
 func Setxattr(path, name string, data []byte) error {
 	return nil
diff --git a/internal/restic/node_windows.go b/internal/restic/node_windows.go
index 7766a1ddf..5875c3ccd 100644
--- a/internal/restic/node_windows.go
+++ b/internal/restic/node_windows.go
@@ -78,10 +78,6 @@ func Listxattr(path string) ([]string, error) {
 	return nil, nil
 }
 
-func IsListxattrPermissionError(_ error) bool {
-	return false
-}
-
 // Setxattr associates name and data together as an attribute of path.
 func Setxattr(path, name string, data []byte) error {
 	return nil
diff --git a/internal/restic/node_xattr.go b/internal/restic/node_xattr.go
index 8b080e74f..0b2d5d552 100644
--- a/internal/restic/node_xattr.go
+++ b/internal/restic/node_xattr.go
@@ -25,14 +25,6 @@ func Listxattr(path string) ([]string, error) {
 	return l, handleXattrErr(err)
 }
 
-func IsListxattrPermissionError(err error) bool {
-	var xerr *xattr.Error
-	if errors.As(err, &xerr) {
-		return xerr.Op == "xattr.list" && errors.Is(xerr.Err, os.ErrPermission)
-	}
-	return false
-}
-
 // Setxattr associates name and data together as an attribute of path.
 func Setxattr(path, name string, data []byte) error {
 	return handleXattrErr(xattr.LSet(path, name, data))


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index b4b34e62f..d3ac3db2b 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -74,7 +74,7 @@ jobs:
       - name: Get programs (Linux/macOS)
         run: |
           echo "build Go tools"
-          go install github.com/restic/rest-server/cmd/rest-server@master
+          go install github.com/restic/rest-server/cmd/rest-server@latest
 
           echo "install minio server"
           mkdir $HOME/bin
@@ -106,7 +106,7 @@ jobs:
           $ProgressPreference = 'SilentlyContinue'
 
           echo "build Go tools"
-          go install github.com/restic/rest-server/cmd/rest-server@master
+          go install github.com/restic/rest-server/...
 
           echo "install minio server"
           mkdir $Env:USERPROFILE/bin
diff --git a/cmd/restic/cmd_restore_integration_test.go b/cmd/restic/cmd_restore_integration_test.go
index 04c13bc68..806c7584b 100644
--- a/cmd/restic/cmd_restore_integration_test.go
+++ b/cmd/restic/cmd_restore_integration_test.go
@@ -4,13 +4,14 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"math/rand"
+	mrand "math/rand"
 	"os"
 	"path/filepath"
 	"syscall"
 	"testing"
 	"time"
 
+	"github.com/restic/restic/internal/feature"
 	"github.com/restic/restic/internal/filter"
 	"github.com/restic/restic/internal/restic"
 	rtest "github.com/restic/restic/internal/test"
@@ -116,7 +117,7 @@ func TestRestore(t *testing.T) {
 	for i := 0; i < 10; i++ {
 		p := filepath.Join(env.testdata, fmt.Sprintf("foo/bar/testfile%v", i))
 		rtest.OK(t, os.MkdirAll(filepath.Dir(p), 0755))
-		rtest.OK(t, appendRandomData(p, uint(rand.Intn(2<<21))))
+		rtest.OK(t, appendRandomData(p, uint(mrand.Intn(2<<21))))
 	}
 
 	opts := BackupOptions{}
@@ -274,6 +275,7 @@ func TestRestoreNoMetadataOnIgnoredIntermediateDirs(t *testing.T) {
 }
 
 func TestRestoreLocalLayout(t *testing.T) {
+	defer feature.TestSetFlag(t, feature.Flag, feature.DeprecateS3LegacyLayout, false)()
 	env, cleanup := withTestEnvironment(t)
 	defer cleanup()
 
diff --git a/internal/archiver/archiver_test.go b/internal/archiver/archiver_test.go
index b1ea4b6b6..91b26f3dd 100644
--- a/internal/archiver/archiver_test.go
+++ b/internal/archiver/archiver_test.go
@@ -23,12 +23,12 @@ import (
 	"github.com/restic/restic/internal/fs"
 	"github.com/restic/restic/internal/repository"
 	"github.com/restic/restic/internal/restic"
-	rtest "github.com/restic/restic/internal/test"
+	restictest "github.com/restic/restic/internal/test"
 	"golang.org/x/sync/errgroup"
 )
 
 func prepareTempdirRepoSrc(t testing.TB, src TestDir) (string, restic.Repository) {
-	tempdir := rtest.TempDir(t)
+	tempdir := restictest.TempDir(t)
 	repo := repository.TestRepository(t)
 
 	TestCreateFiles(t, tempdir, src)
@@ -133,7 +133,7 @@ func TestArchiverSaveFile(t *testing.T) {
 	var tests = []TestFile{
 		{Content: ""},
 		{Content: "foo"},
-		{Content: string(rtest.Random(23, 12*1024*1024+1287898))},
+		{Content: string(restictest.Random(23, 12*1024*1024+1287898))},
 	}
 
 	for _, testfile := range tests {
@@ -166,7 +166,7 @@ func TestArchiverSaveFileReaderFS(t *testing.T) {
 		Data string
 	}{
 		{Data: "foo"},
-		{Data: string(rtest.Random(23, 12*1024*1024+1287898))},
+		{Data: string(restictest.Random(23, 12*1024*1024+1287898))},
 	}
 
 	for _, test := range tests {
@@ -208,7 +208,7 @@ func TestArchiverSave(t *testing.T) {
 	var tests = []TestFile{
 		{Content: ""},
 		{Content: "foo"},
-		{Content: string(rtest.Random(23, 12*1024*1024+1287898))},
+		{Content: string(restictest.Random(23, 12*1024*1024+1287898))},
 	}
 
 	for _, testfile := range tests {
@@ -277,7 +277,7 @@ func TestArchiverSaveReaderFS(t *testing.T) {
 		Data string
 	}{
 		{Data: "foo"},
-		{Data: string(rtest.Random(23, 12*1024*1024+1287898))},
+		{Data: string(restictest.Random(23, 12*1024*1024+1287898))},
 	}
 
 	for _, test := range tests {
@@ -354,7 +354,7 @@ func TestArchiverSaveReaderFS(t *testing.T) {
 func BenchmarkArchiverSaveFileSmall(b *testing.B) {
 	const fileSize = 4 * 1024
 	d := TestDir{"file": TestFile{
-		Content: string(rtest.Random(23, fileSize)),
+		Content: string(restictest.Random(23, fileSize)),
 	}}
 
 	b.SetBytes(fileSize)
@@ -386,7 +386,7 @@ func BenchmarkArchiverSaveFileSmall(b *testing.B) {
 func BenchmarkArchiverSaveFileLarge(b *testing.B) {
 	const fileSize = 40*1024*1024 + 1287898
 	d := TestDir{"file": TestFile{
-		Content: string(rtest.Random(23, fileSize)),
+		Content: string(restictest.Random(23, fileSize)),
 	}}
 
 	b.SetBytes(fileSize)
@@ -462,14 +462,14 @@ func appendToFile(t testing.TB, filename string, data []byte) {
 }
 
 func TestArchiverSaveFileIncremental(t *testing.T) {
-	tempdir := rtest.TempDir(t)
+	tempdir := restictest.TempDir(t)
 
 	repo := &blobCountingRepo{
 		Repository: repository.TestRepository(t),
 		saved:      make(map[restic.BlobHandle]uint),
 	}
 
-	data := rtest.Random(23, 512*1024+887898)
+	data := restictest.Random(23, 512*1024+887898)
 	testfile := filepath.Join(tempdir, "testfile")
 
 	for i := 0; i < 3; i++ {
@@ -512,12 +512,12 @@ func chmodTwice(t testing.TB, name string) {
 	// POSIX says that ctime is updated "even if the file status does not
 	// change", but let's make sure it does change, just in case.
 	err := os.Chmod(name, 0700)
-	rtest.OK(t, err)
+	restictest.OK(t, err)
 
 	sleep()
 
 	err = os.Chmod(name, 0600)
-	rtest.OK(t, err)
+	restictest.OK(t, err)
 }
 
 func lstat(t testing.TB, name string) os.FileInfo {
@@ -556,7 +556,7 @@ func rename(t testing.TB, oldname, newname string) {
 }
 
 func nodeFromFI(t testing.TB, filename string, fi os.FileInfo) *restic.Node {
-	node, err := restic.NodeFromFileInfo(filename, fi, false)
+	node, err := restic.NodeFromFileInfo(filename, fi)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -676,7 +676,7 @@ func TestFileChanged(t *testing.T) {
 				t.Skip("don't run test on Windows")
 			}
 
-			tempdir := rtest.TempDir(t)
+			tempdir := restictest.TempDir(t)
 
 			filename := filepath.Join(tempdir, "file")
 			content := defaultContent
@@ -712,7 +712,7 @@ func TestFileChanged(t *testing.T) {
 }
 
 func TestFilChangedSpecialCases(t *testing.T) {
-	tempdir := rtest.TempDir(t)
+	tempdir := restictest.TempDir(t)
 
 	filename := filepath.Join(tempdir, "file")
 	content := []byte("foobar")
@@ -746,12 +746,12 @@ func TestArchiverSaveDir(t *testing.T) {
 	}{
 		{
 			src: TestDir{
-				"targetfile": TestFile{Content: string(rtest.Random(888, 2*1024*1024+5000))},
+				"targetfile": TestFile{Content: string(restictest.Random(888, 2*1024*1024+5000))},
 			},
 			target: ".",
 			want: TestDir{
 				"targetdir": TestDir{
-					"targetfile": TestFile{Content: string(rtest.Random(888, 2*1024*1024+5000))},
+					"targetfile": TestFile{Content: string(restictest.Random(888, 2*1024*1024+5000))},
 				},
 			},
 		},
@@ -761,8 +761,8 @@ func TestArchiverSaveDir(t *testing.T) {
 					"foo":        TestFile{Content: "foo"},
 					"emptyfile":  TestFile{Content: ""},
 					"bar":        TestFile{Content: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"},
-					"largefile":  TestFile{Content: string(rtest.Random(888, 2*1024*1024+5000))},
-					"largerfile": TestFile{Content: string(rtest.Random(234, 5*1024*1024+5000))},
+					"largefile":  TestFile{Content: string(restictest.Random(888, 2*1024*1024+5000))},
+					"largerfile": TestFile{Content: string(restictest.Random(234, 5*1024*1024+5000))},
 				},
 			},
 			target: "targetdir",
@@ -841,7 +841,7 @@ func TestArchiverSaveDir(t *testing.T) {
 				chdir = filepath.Join(chdir, test.chdir)
 			}
 
-			back := rtest.Chdir(t, chdir)
+			back := restictest.Chdir(t, chdir)
 			defer back()
 
 			fi, err := fs.Lstat(test.target)
@@ -899,7 +899,7 @@ func TestArchiverSaveDir(t *testing.T) {
 }
 
 func TestArchiverSaveDirIncremental(t *testing.T) {
-	tempdir := rtest.TempDir(t)
+	tempdir := restictest.TempDir(t)
 
 	repo := &blobCountingRepo{
 		Repository: repository.TestRepository(t),
@@ -989,7 +989,7 @@ func TestArchiverSaveDirIncremental(t *testing.T) {
 func bothZeroOrNeither(tb testing.TB, exp, act uint64) {
 	tb.Helper()
 	if (exp == 0 && act != 0) || (exp != 0 && act == 0) {
-		rtest.Equals(tb, exp, act)
+		restictest.Equals(tb, exp, act)
 	}
 }
 
@@ -1113,7 +1113,7 @@ func TestArchiverSaveTree(t *testing.T) {
 			arch.runWorkers(ctx, wg)
 			arch.summary = &Summary{}
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			if test.prepare != nil {
@@ -1158,9 +1158,9 @@ func TestArchiverSaveTree(t *testing.T) {
 			bothZeroOrNeither(t, test.stat.DataSize, stat.DataSize)
 			bothZeroOrNeither(t, test.stat.DataSizeInRepo, stat.DataSizeInRepo)
 			bothZeroOrNeither(t, test.stat.TreeSizeInRepo, stat.TreeSizeInRepo)
-			rtest.Equals(t, test.stat.ProcessedBytes, stat.ProcessedBytes)
-			rtest.Equals(t, test.stat.Files, stat.Files)
-			rtest.Equals(t, test.stat.Dirs, stat.Dirs)
+			restictest.Equals(t, test.stat.ProcessedBytes, stat.ProcessedBytes)
+			restictest.Equals(t, test.stat.Files, stat.Files)
+			restictest.Equals(t, test.stat.Dirs, stat.Dirs)
 		})
 	}
 }
@@ -1408,7 +1408,7 @@ func TestArchiverSnapshot(t *testing.T) {
 				chdir = filepath.Join(chdir, filepath.FromSlash(test.chdir))
 			}
 
-			back := rtest.Chdir(t, chdir)
+			back := restictest.Chdir(t, chdir)
 			defer back()
 
 			var targets []string
@@ -1561,7 +1561,7 @@ func TestArchiverSnapshotSelect(t *testing.T) {
 			arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
 			arch.Select = test.selFn
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			targets := []string{"."}
@@ -1639,14 +1639,14 @@ func (f MockFile) Read(p []byte) (int, error) {
 }
 
 func checkSnapshotStats(t *testing.T, sn *restic.Snapshot, stat Summary) {
-	rtest.Equals(t, stat.Files.New, sn.Summary.FilesNew)
-	rtest.Equals(t, stat.Files.Changed, sn.Summary.FilesChanged)
-	rtest.Equals(t, stat.Files.Unchanged, sn.Summary.FilesUnmodified)
-	rtest.Equals(t, stat.Dirs.New, sn.Summary.DirsNew)
-	rtest.Equals(t, stat.Dirs.Changed, sn.Summary.DirsChanged)
-	rtest.Equals(t, stat.Dirs.Unchanged, sn.Summary.DirsUnmodified)
-	rtest.Equals(t, stat.ProcessedBytes, sn.Summary.TotalBytesProcessed)
-	rtest.Equals(t, stat.Files.New+stat.Files.Changed+stat.Files.Unchanged, sn.Summary.TotalFilesProcessed)
+	restictest.Equals(t, stat.Files.New, sn.Summary.FilesNew)
+	restictest.Equals(t, stat.Files.Changed, sn.Summary.FilesChanged)
+	restictest.Equals(t, stat.Files.Unchanged, sn.Summary.FilesUnmodified)
+	restictest.Equals(t, stat.Dirs.New, sn.Summary.DirsNew)
+	restictest.Equals(t, stat.Dirs.Changed, sn.Summary.DirsChanged)
+	restictest.Equals(t, stat.Dirs.Unchanged, sn.Summary.DirsUnmodified)
+	restictest.Equals(t, stat.ProcessedBytes, sn.Summary.TotalBytesProcessed)
+	restictest.Equals(t, stat.Files.New+stat.Files.Changed+stat.Files.Unchanged, sn.Summary.TotalFilesProcessed)
 	bothZeroOrNeither(t, uint64(stat.DataBlobs), uint64(sn.Summary.DataBlobs))
 	bothZeroOrNeither(t, uint64(stat.TreeBlobs), uint64(sn.Summary.TreeBlobs))
 	bothZeroOrNeither(t, uint64(stat.DataSize+stat.TreeSize), uint64(sn.Summary.DataAdded))
@@ -1662,7 +1662,7 @@ func TestArchiverParent(t *testing.T) {
 	}{
 		{
 			src: TestDir{
-				"targetfile": TestFile{Content: string(rtest.Random(888, 2*1024*1024+5000))},
+				"targetfile": TestFile{Content: string(restictest.Random(888, 2*1024*1024+5000))},
 			},
 			statInitial: Summary{
 				Files:          ChangeStats{1, 0, 0},
@@ -1679,8 +1679,8 @@ func TestArchiverParent(t *testing.T) {
 		{
 			src: TestDir{
 				"targetDir": TestDir{
-					"targetfile":  TestFile{Content: string(rtest.Random(888, 1234))},
-					"targetfile2": TestFile{Content: string(rtest.Random(888, 1235))},
+					"targetfile":  TestFile{Content: string(restictest.Random(888, 1234))},
+					"targetfile2": TestFile{Content: string(restictest.Random(888, 1235))},
 				},
 			},
 			statInitial: Summary{
@@ -1698,9 +1698,9 @@ func TestArchiverParent(t *testing.T) {
 		{
 			src: TestDir{
 				"targetDir": TestDir{
-					"targetfile": TestFile{Content: string(rtest.Random(888, 1234))},
+					"targetfile": TestFile{Content: string(restictest.Random(888, 1234))},
 				},
-				"targetfile2": TestFile{Content: string(rtest.Random(888, 1235))},
+				"targetfile2": TestFile{Content: string(restictest.Random(888, 1235))},
 			},
 			modify: func(path string) {
 				remove(t, filepath.Join(path, "targetDir", "targetfile"))
@@ -1735,7 +1735,7 @@ func TestArchiverParent(t *testing.T) {
 
 			arch := New(repo, testFS, Options{})
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			firstSnapshot, firstSnapshotID, summary, err := arch.Snapshot(ctx, []string{"."}, SnapshotOptions{Time: time.Now()})
@@ -1763,9 +1763,9 @@ func TestArchiverParent(t *testing.T) {
 				}
 				return nil
 			})
-			rtest.Equals(t, test.statInitial.Files, summary.Files)
-			rtest.Equals(t, test.statInitial.Dirs, summary.Dirs)
-			rtest.Equals(t, test.statInitial.ProcessedBytes, summary.ProcessedBytes)
+			restictest.Equals(t, test.statInitial.Files, summary.Files)
+			restictest.Equals(t, test.statInitial.Dirs, summary.Dirs)
+			restictest.Equals(t, test.statInitial.ProcessedBytes, summary.ProcessedBytes)
 			checkSnapshotStats(t, firstSnapshot, test.statInitial)
 
 			if test.modify != nil {
@@ -1784,11 +1784,11 @@ func TestArchiverParent(t *testing.T) {
 
 			if test.modify == nil {
 				// check that no files were read this time
-				rtest.Equals(t, map[string]int{}, testFS.bytesRead)
+				restictest.Equals(t, map[string]int{}, testFS.bytesRead)
 			}
-			rtest.Equals(t, test.statSecond.Files, summary.Files)
-			rtest.Equals(t, test.statSecond.Dirs, summary.Dirs)
-			rtest.Equals(t, test.statSecond.ProcessedBytes, summary.ProcessedBytes)
+			restictest.Equals(t, test.statSecond.Files, summary.Files)
+			restictest.Equals(t, test.statSecond.Dirs, summary.Dirs)
+			restictest.Equals(t, test.statSecond.ProcessedBytes, summary.ProcessedBytes)
 			checkSnapshotStats(t, secondSnapshot, test.statSecond)
 
 			t.Logf("second backup saved as %v", secondSnapshotID.Str())
@@ -1894,7 +1894,7 @@ func TestArchiverErrorReporting(t *testing.T) {
 
 			tempdir, repo := prepareTempdirRepoSrc(t, test.src)
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			if test.prepare != nil {
@@ -1964,7 +1964,7 @@ func TestArchiverContextCanceled(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	cancel()
 
-	tempdir := rtest.TempDir(t)
+	tempdir := restictest.TempDir(t)
 	TestCreateFiles(t, tempdir, TestDir{
 		"targetfile": TestFile{Content: "foobar"},
 	})
@@ -1972,7 +1972,7 @@ func TestArchiverContextCanceled(t *testing.T) {
 	// Ensure that the archiver itself reports the canceled context and not just the backend
 	repo := repository.TestRepositoryWithBackend(t, &noCancelBackend{mem.New()}, 0, repository.Options{})
 
-	back := rtest.Chdir(t, tempdir)
+	back := restictest.Chdir(t, tempdir)
 	defer back()
 
 	arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
@@ -2058,16 +2058,16 @@ func TestArchiverAbortEarlyOnError(t *testing.T) {
 		{
 			src: TestDir{
 				"dir": TestDir{
-					"file0": TestFile{Content: string(rtest.Random(0, 1024))},
-					"file1": TestFile{Content: string(rtest.Random(1, 1024))},
-					"file2": TestFile{Content: string(rtest.Random(2, 1024))},
-					"file3": TestFile{Content: string(rtest.Random(3, 1024))},
-					"file4": TestFile{Content: string(rtest.Random(4, 1024))},
-					"file5": TestFile{Content: string(rtest.Random(5, 1024))},
-					"file6": TestFile{Content: string(rtest.Random(6, 1024))},
-					"file7": TestFile{Content: string(rtest.Random(7, 1024))},
-					"file8": TestFile{Content: string(rtest.Random(8, 1024))},
-					"file9": TestFile{Content: string(rtest.Random(9, 1024))},
+					"file0": TestFile{Content: string(restictest.Random(0, 1024))},
+					"file1": TestFile{Content: string(restictest.Random(1, 1024))},
+					"file2": TestFile{Content: string(restictest.Random(2, 1024))},
+					"file3": TestFile{Content: string(restictest.Random(3, 1024))},
+					"file4": TestFile{Content: string(restictest.Random(4, 1024))},
+					"file5": TestFile{Content: string(restictest.Random(5, 1024))},
+					"file6": TestFile{Content: string(restictest.Random(6, 1024))},
+					"file7": TestFile{Content: string(restictest.Random(7, 1024))},
+					"file8": TestFile{Content: string(restictest.Random(8, 1024))},
+					"file9": TestFile{Content: string(restictest.Random(9, 1024))},
 				},
 			},
 			wantOpen: map[string]uint{
@@ -2092,7 +2092,7 @@ func TestArchiverAbortEarlyOnError(t *testing.T) {
 
 			tempdir, repo := prepareTempdirRepoSrc(t, test.src)
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			testFS := &TrackFS{
@@ -2225,12 +2225,12 @@ func TestMetadataChanged(t *testing.T) {
 
 	tempdir, repo := prepareTempdirRepoSrc(t, files)
 
-	back := rtest.Chdir(t, tempdir)
+	back := restictest.Chdir(t, tempdir)
 	defer back()
 
 	// get metadata
 	fi := lstat(t, "testfile")
-	want, err := restic.NodeFromFileInfo("testfile", fi, false)
+	want, err := restic.NodeFromFileInfo("testfile", fi)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2300,7 +2300,7 @@ func TestRacyFileSwap(t *testing.T) {
 
 	tempdir, repo := prepareTempdirRepoSrc(t, files)
 
-	back := rtest.Chdir(t, tempdir)
+	back := restictest.Chdir(t, tempdir)
 	defer back()
 
 	// get metadata of current folder
diff --git a/internal/archiver/archiver_unix_test.go b/internal/archiver/archiver_unix_test.go
index a6b1aad2e..2552b23e1 100644
--- a/internal/archiver/archiver_unix_test.go
+++ b/internal/archiver/archiver_unix_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/restic/restic/internal/feature"
 	"github.com/restic/restic/internal/fs"
 	"github.com/restic/restic/internal/restic"
-	rtest "github.com/restic/restic/internal/test"
+	restictest "github.com/restic/restic/internal/test"
 )
 
 type wrappedFileInfo struct {
@@ -48,8 +48,8 @@ func wrapFileInfo(fi os.FileInfo) os.FileInfo {
 
 func statAndSnapshot(t *testing.T, repo restic.Repository, name string) (*restic.Node, *restic.Node) {
 	fi := lstat(t, name)
-	want, err := restic.NodeFromFileInfo(name, fi, false)
-	rtest.OK(t, err)
+	want, err := restic.NodeFromFileInfo(name, fi)
+	restictest.OK(t, err)
 
 	_, node := snapshot(t, repo, fs.Local{}, nil, name)
 	return want, node
@@ -73,17 +73,17 @@ func TestHardlinkMetadata(t *testing.T) {
 
 	tempdir, repo := prepareTempdirRepoSrc(t, files)
 
-	back := rtest.Chdir(t, tempdir)
+	back := restictest.Chdir(t, tempdir)
 	defer back()
 
 	want, node := statAndSnapshot(t, repo, "testlink")
-	rtest.Assert(t, node.DeviceID == want.DeviceID, "device id mismatch expected %v got %v", want.DeviceID, node.DeviceID)
-	rtest.Assert(t, node.Links == want.Links, "link count mismatch expected %v got %v", want.Links, node.Links)
-	rtest.Assert(t, node.Inode == want.Inode, "inode mismatch expected %v got %v", want.Inode, node.Inode)
+	restictest.Assert(t, node.DeviceID == want.DeviceID, "device id mismatch expected %v got %v", want.DeviceID, node.DeviceID)
+	restictest.Assert(t, node.Links == want.Links, "link count mismatch expected %v got %v", want.Links, node.Links)
+	restictest.Assert(t, node.Inode == want.Inode, "inode mismatch expected %v got %v", want.Inode, node.Inode)
 
 	_, node = statAndSnapshot(t, repo, "testfile")
-	rtest.Assert(t, node.DeviceID == 0, "device id mismatch for testfile expected %v got %v", 0, node.DeviceID)
+	restictest.Assert(t, node.DeviceID == 0, "device id mismatch for testfile expected %v got %v", 0, node.DeviceID)
 
 	_, node = statAndSnapshot(t, repo, "testdir")
-	rtest.Assert(t, node.DeviceID == 0, "device id mismatch for testdir expected %v got %v", 0, node.DeviceID)
+	restictest.Assert(t, node.DeviceID == 0, "device id mismatch for testdir expected %v got %v", 0, node.DeviceID)
 }
diff --git a/internal/archiver/file_saver_test.go b/internal/archiver/file_saver_test.go
index 409bdedd0..ced9d796e 100644
--- a/internal/archiver/file_saver_test.go
+++ b/internal/archiver/file_saver_test.go
@@ -49,8 +49,8 @@ func startFileSaver(ctx context.Context, t testing.TB) (*FileSaver, context.Cont
 	}
 
 	s := NewFileSaver(ctx, wg, saveBlob, pol, workers, workers)
-	s.NodeFromFileInfo = func(snPath, filename string, fi os.FileInfo, ignoreXattrListError bool) (*restic.Node, error) {
-		return restic.NodeFromFileInfo(filename, fi, ignoreXattrListError)
+	s.NodeFromFileInfo = func(snPath, filename string, fi os.FileInfo) (*restic.Node, error) {
+		return restic.NodeFromFileInfo(filename, fi)
 	}
 
 	return s, ctx, wg
diff --git a/internal/archiver/scanner_test.go b/internal/archiver/scanner_test.go
index b5b7057b8..1b4cd1f7f 100644
--- a/internal/archiver/scanner_test.go
+++ b/internal/archiver/scanner_test.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/restic/restic/internal/fs"
-	rtest "github.com/restic/restic/internal/test"
+	restictest "github.com/restic/restic/internal/test"
 )
 
 func TestScanner(t *testing.T) {
@@ -81,10 +81,10 @@ func TestScanner(t *testing.T) {
 			ctx, cancel := context.WithCancel(context.Background())
 			defer cancel()
 
-			tempdir := rtest.TempDir(t)
+			tempdir := restictest.TempDir(t)
 			TestCreateFiles(t, tempdir, test.src)
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			cur, err := os.Getwd()
@@ -216,10 +216,10 @@ func TestScannerError(t *testing.T) {
 			ctx, cancel := context.WithCancel(context.Background())
 			defer cancel()
 
-			tempdir := rtest.TempDir(t)
+			tempdir := restictest.TempDir(t)
 			TestCreateFiles(t, tempdir, test.src)
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			cur, err := os.Getwd()
@@ -288,10 +288,10 @@ func TestScannerCancel(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	tempdir := rtest.TempDir(t)
+	tempdir := restictest.TempDir(t)
 	TestCreateFiles(t, tempdir, src)
 
-	back := rtest.Chdir(t, tempdir)
+	back := restictest.Chdir(t, tempdir)
 	defer back()
 
 	cur, err := os.Getwd()
diff --git a/internal/archiver/testing_test.go b/internal/archiver/testing_test.go
index ff3bd3668..e48b41ec7 100644
--- a/internal/archiver/testing_test.go
+++ b/internal/archiver/testing_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/restic/restic/internal/fs"
 	"github.com/restic/restic/internal/repository"
-	rtest "github.com/restic/restic/internal/test"
+	restictest "github.com/restic/restic/internal/test"
 )
 
 // MockT passes through all logging functions from T, but catches Fail(),
@@ -101,7 +101,7 @@ func TestTestCreateFiles(t *testing.T) {
 	}
 
 	for i, test := range tests {
-		tempdir := rtest.TempDir(t)
+		tempdir := restictest.TempDir(t)
 
 		t.Run("", func(t *testing.T) {
 			tempdir := filepath.Join(tempdir, fmt.Sprintf("test-%d", i))
@@ -191,7 +191,7 @@ func TestTestWalkFiles(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run("", func(t *testing.T) {
-			tempdir := rtest.TempDir(t)
+			tempdir := restictest.TempDir(t)
 
 			got := make(map[string]string)
 
@@ -321,7 +321,7 @@ func TestTestEnsureFiles(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run("", func(t *testing.T) {
-			tempdir := rtest.TempDir(t)
+			tempdir := restictest.TempDir(t)
 			createFilesAt(t, tempdir, test.files)
 
 			subtestT := testing.TB(t)
@@ -452,7 +452,7 @@ func TestTestEnsureSnapshot(t *testing.T) {
 			ctx, cancel := context.WithCancel(context.Background())
 			defer cancel()
 
-			tempdir := rtest.TempDir(t)
+			tempdir := restictest.TempDir(t)
 
 			targetDir := filepath.Join(tempdir, "target")
 			err := fs.Mkdir(targetDir, 0700)
@@ -462,7 +462,7 @@ func TestTestEnsureSnapshot(t *testing.T) {
 
 			createFilesAt(t, targetDir, test.files)
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			repo := repository.TestRepository(t)
diff --git a/internal/archiver/tree_test.go b/internal/archiver/tree_test.go
index a9d2d97ff..7852a4c2e 100644
--- a/internal/archiver/tree_test.go
+++ b/internal/archiver/tree_test.go
@@ -8,7 +8,7 @@ import (
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/restic/restic/internal/fs"
-	rtest "github.com/restic/restic/internal/test"
+	restictest "github.com/restic/restic/internal/test"
 )
 
 // debug.Log requires Tree.String.
@@ -439,10 +439,10 @@ func TestTree(t *testing.T) {
 				t.Skip("skip test on unix")
 			}
 
-			tempdir := rtest.TempDir(t)
+			tempdir := restictest.TempDir(t)
 			TestCreateFiles(t, tempdir, test.src)
 
-			back := rtest.Chdir(t, tempdir)
+			back := restictest.Chdir(t, tempdir)
 			defer back()
 
 			tree, err := NewTree(fs.Local{}, test.targets)
diff --git a/internal/backend/layout/layout_test.go b/internal/backend/layout/layout_test.go
index 998f5aeb6..55a0749c9 100644
--- a/internal/backend/layout/layout_test.go
+++ b/internal/backend/layout/layout_test.go
@@ -10,6 +10,7 @@ import (
 	"testing"
 
 	"github.com/restic/restic/internal/backend"
+	"github.com/restic/restic/internal/feature"
 	rtest "github.com/restic/restic/internal/test"
 )
 
@@ -352,6 +353,7 @@ func TestS3LegacyLayout(t *testing.T) {
 }
 
 func TestDetectLayout(t *testing.T) {
+	defer feature.TestSetFlag(t, feature.Flag, feature.DeprecateS3LegacyLayout, false)()
 	path := rtest.TempDir(t)
 
 	var tests = []struct {
@@ -389,6 +391,7 @@ func TestDetectLayout(t *testing.T) {
 }
 
 func TestParseLayout(t *testing.T) {
+	defer feature.TestSetFlag(t, feature.Flag, feature.DeprecateS3LegacyLayout, false)()
 	path := rtest.TempDir(t)
 
 	var tests = []struct {
diff --git a/internal/backend/local/layout_test.go b/internal/backend/local/layout_test.go
index 46f3996bb..00c91376a 100644
--- a/internal/backend/local/layout_test.go
+++ b/internal/backend/local/layout_test.go
@@ -6,10 +6,12 @@ import (
 	"testing"
 
 	"github.com/restic/restic/internal/backend"
+	"github.com/restic/restic/internal/feature"
 	rtest "github.com/restic/restic/internal/test"
 )
 
 func TestLayout(t *testing.T) {
+	defer feature.TestSetFlag(t, feature.Flag, feature.DeprecateS3LegacyLayout, false)()
 	path := rtest.TempDir(t)
 
 	var tests = []struct {
diff --git a/internal/backend/rest/config_test.go b/internal/backend/rest/config_test.go
index 13a1ebb13..23ea9095b 100644
--- a/internal/backend/rest/config_test.go
+++ b/internal/backend/rest/config_test.go
@@ -31,13 +31,6 @@ var configTests = []test.ConfigTestData[Config]{
 			Connections: 5,
 		},
 	},
-	{
-		S: "rest:http+unix:///tmp/rest.socket:/my_backup_repo/",
-		Cfg: Config{
-			URL:         parseURL("http+unix:///tmp/rest.socket:/my_backup_repo/"),
-			Connections: 5,
-		},
-	},
 }
 
 func TestParseConfig(t *testing.T) {
diff --git a/internal/backend/rest/rest_test.go b/internal/backend/rest/rest_test.go
index 93b9a103e..6a5b4f8a5 100644
--- a/internal/backend/rest/rest_test.go
+++ b/internal/backend/rest/rest_test.go
@@ -1,18 +1,11 @@
-//go:build go1.20
-// +build go1.20
-
 package rest_test
 
 import (
-	"bufio"
 	"context"
-	"fmt"
+	"net"
 	"net/url"
 	"os"
 	"os/exec"
-	"regexp"
-	"strings"
-	"syscall"
 	"testing"
 	"time"
 
@@ -21,133 +14,54 @@ import (
 	rtest "github.com/restic/restic/internal/test"
 )
 
-var (
-	serverStartedRE = regexp.MustCompile("^start server on (.*)$")
-)
-
-func runRESTServer(ctx context.Context, t testing.TB, dir, reqListenAddr string) (*url.URL, func()) {
+func runRESTServer(ctx context.Context, t testing.TB, dir string) (*url.URL, func()) {
 	srv, err := exec.LookPath("rest-server")
 	if err != nil {
 		t.Skip(err)
 	}
 
-	// create our own context, so that our cleanup can cancel and wait for completion
-	// this will ensure any open ports, open unix sockets etc are properly closed
-	processCtx, cancel := context.WithCancel(ctx)
-	cmd := exec.CommandContext(processCtx, srv, "--no-auth", "--path", dir, "--listen", reqListenAddr)
-
-	// this cancel func is called by when the process context is done
-	cmd.Cancel = func() error {
-		// we execute in a Go-routine as we know the caller will
-		// be waiting on a .Wait() regardless
-		go func() {
-			// try to send a graceful termination signal
-			if cmd.Process.Signal(syscall.SIGTERM) == nil {
-				// if we succeed, then wait a few seconds
-				time.Sleep(2 * time.Second)
-			}
-			// and then make sure it's killed either way, ignoring any error code
-			_ = cmd.Process.Kill()
-		}()
-		return nil
-	}
-
-	// this is the cleanup function that we return the caller,
-	// which will cancel our process context, and then wait for it to finish
-	cleanup := func() {
-		cancel()
-		_ = cmd.Wait()
-	}
-
-	// but in-case we don't finish this method, e.g. by calling t.Fatal()
-	// we also defer a call to clean it up ourselves, guarded by a flag to
-	// indicate that we returned the function to the caller to deal with.
-	callerWillCleanUp := false
-	defer func() {
-		if !callerWillCleanUp {
-			cleanup()
-		}
-	}()
-
-	// send stdout to our std out
+	cmd := exec.CommandContext(ctx, srv, "--no-auth", "--path", dir)
 	cmd.Stdout = os.Stdout
-
-	// capture stderr with a pipe, as we want to examine this output
-	// to determine when the server is started and listening.
-	cmdErr, err := cmd.StderrPipe()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// start the rest-server
+	cmd.Stderr = os.Stdout
 	if err := cmd.Start(); err != nil {
 		t.Fatal(err)
 	}
 
-	// create a channel to receive the actual listen address on
-	listenAddrCh := make(chan string)
-	go func() {
-		defer close(listenAddrCh)
-		matched := false
-		br := bufio.NewReader(cmdErr)
-		for {
-			line, err := br.ReadString('\n')
-			if err != nil {
-				// we ignore errors, as code that relies on this
-				// will happily fail via timeout and empty closed
-				// channel.
-				return
-			}
-
-			line = strings.Trim(line, "\r\n")
-			if !matched {
-				// look for the server started message, and return the address
-				// that it's listening on
-				matchedServerListen := serverStartedRE.FindSubmatch([]byte(line))
-				if len(matchedServerListen) == 2 {
-					listenAddrCh <- string(matchedServerListen[1])
-					matched = true
-				}
-			}
-			fmt.Fprintln(os.Stdout, line) // print all output to console
+	// wait until the TCP port is reachable
+	var success bool
+	for i := 0; i < 10; i++ {
+		time.Sleep(200 * time.Millisecond)
+
+		c, err := net.Dial("tcp", "localhost:8000")
+		if err != nil {
+			continue
 		}
-	}()
 
-	// wait for us to get an address,
-	// or the parent context to cancel,
-	// or for us to timeout
-	var actualListenAddr string
-	select {
-	case <-processCtx.Done():
-		t.Fatal(context.Canceled)
-	case <-time.NewTimer(2 * time.Second).C:
-		t.Fatal(context.DeadlineExceeded)
-	case a, ok := <-listenAddrCh:
-		if !ok {
-			t.Fatal(context.Canceled)
+		success = true
+		if err := c.Close(); err != nil {
+			t.Fatal(err)
 		}
-		actualListenAddr = a
 	}
 
-	// this translate the address that the server is listening on
-	// to a URL suitable for us to connect to
-	var addrToConnectTo string
-	if strings.HasPrefix(reqListenAddr, "unix:") {
-		addrToConnectTo = fmt.Sprintf("http+unix://%s:/restic-test/", actualListenAddr)
-	} else {
-		// while we may listen on 0.0.0.0, we connect to localhost
-		addrToConnectTo = fmt.Sprintf("http://%s/restic-test/", strings.Replace(actualListenAddr, "0.0.0.0", "localhost", 1))
+	if !success {
+		t.Fatal("unable to connect to rest server")
+		return nil, nil
 	}
 
-	// parse to a URL
-	url, err := url.Parse(addrToConnectTo)
+	url, err := url.Parse("http://localhost:8000/restic-test/")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	// indicate that we've completed successfully, and that the caller
-	// is responsible for calling cleanup
-	callerWillCleanUp = true
+	cleanup := func() {
+		if err := cmd.Process.Kill(); err != nil {
+			t.Fatal(err)
+		}
+
+		// ignore errors, we've killed the process
+		_ = cmd.Wait()
+	}
+
 	return url, cleanup
 }
 
@@ -177,7 +91,7 @@ func TestBackendREST(t *testing.T) {
 	defer cancel()
 
 	dir := rtest.TempDir(t)
-	serverURL, cleanup := runRESTServer(ctx, t, dir, ":0")
+	serverURL, cleanup := runRESTServer(ctx, t, dir)
 	defer cleanup()
 
 	newTestSuite(serverURL, false).RunTests(t)
@@ -202,7 +116,7 @@ func BenchmarkBackendREST(t *testing.B) {
 	defer cancel()
 
 	dir := rtest.TempDir(t)
-	serverURL, cleanup := runRESTServer(ctx, t, dir, ":0")
+	serverURL, cleanup := runRESTServer(ctx, t, dir)
 	defer cleanup()
 
 	newTestSuite(serverURL, false).RunBenchmarks(t)
diff --git a/internal/backend/rest/rest_unix_test.go b/internal/backend/rest/rest_unix_test.go
deleted file mode 100644
index 85ef7a73d..000000000
--- a/internal/backend/rest/rest_unix_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-//go:build !windows && go1.20
-// +build !windows,go1.20
-
-package rest_test
-
-import (
-	"context"
-	"fmt"
-	"path"
-	"testing"
-
-	rtest "github.com/restic/restic/internal/test"
-)
-
-func TestBackendRESTWithUnixSocket(t *testing.T) {
-	defer func() {
-		if t.Skipped() {
-			rtest.SkipDisallowed(t, "restic/backend/rest.TestBackendREST")
-		}
-	}()
-
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	dir := rtest.TempDir(t)
-	serverURL, cleanup := runRESTServer(ctx, t, path.Join(dir, "data"), fmt.Sprintf("unix:%s", path.Join(dir, "sock")))
-	defer cleanup()
-
-	newTestSuite(serverURL, false).RunTests(t)
-}
diff --git a/internal/backend/sftp/layout_test.go b/internal/backend/sftp/layout_test.go
index 9cf24a753..8bb7eac01 100644
--- a/internal/backend/sftp/layout_test.go
+++ b/internal/backend/sftp/layout_test.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/restic/restic/internal/backend"
 	"github.com/restic/restic/internal/backend/sftp"
+	"github.com/restic/restic/internal/feature"
 	rtest "github.com/restic/restic/internal/test"
 )
 
@@ -16,6 +17,7 @@ func TestLayout(t *testing.T) {
 		t.Skip("sftp server binary not available")
 	}
 
+	defer feature.TestSetFlag(t, feature.Flag, feature.DeprecateS3LegacyLayout, false)()
 	path := rtest.TempDir(t)
 
 	var tests = []struct {
diff --git a/internal/fs/stat_test.go b/internal/fs/stat_test.go
index d52415c1d..a5ec77c7a 100644
--- a/internal/fs/stat_test.go
+++ b/internal/fs/stat_test.go
@@ -5,11 +5,11 @@ import (
 	"path/filepath"
 	"testing"
 
-	rtest "github.com/restic/restic/internal/test"
+	restictest "github.com/restic/restic/internal/test"
 )
 
 func TestExtendedStat(t *testing.T) {
-	tempdir := rtest.TempDir(t)
+	tempdir := restictest.TempDir(t)
 	filename := filepath.Join(tempdir, "file")
 	err := os.WriteFile(filename, []byte("foobar"), 0640)
 	if err != nil {
diff --git a/internal/restic/node_test.go b/internal/restic/node_test.go
index ea271faab..d9fa02ac8 100644
--- a/internal/restic/node_test.go
+++ b/internal/restic/node_test.go
@@ -11,7 +11,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/google/go-cmp/cmp"
 	"github.com/restic/restic/internal/test"
 	rtest "github.com/restic/restic/internal/test"
 )
@@ -32,7 +31,7 @@ func BenchmarkNodeFillUser(t *testing.B) {
 	t.ResetTimer()
 
 	for i := 0; i < t.N; i++ {
-		_, err := NodeFromFileInfo(path, fi, false)
+		_, err := NodeFromFileInfo(path, fi)
 		rtest.OK(t, err)
 	}
 
@@ -56,7 +55,7 @@ func BenchmarkNodeFromFileInfo(t *testing.B) {
 	t.ResetTimer()
 
 	for i := 0; i < t.N; i++ {
-		_, err := NodeFromFileInfo(path, fi, false)
+		_, err := NodeFromFileInfo(path, fi)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -228,11 +227,8 @@ func TestNodeRestoreAt(t *testing.T) {
 			fi, err := os.Lstat(nodePath)
 			rtest.OK(t, err)
 
-			n2, err := NodeFromFileInfo(nodePath, fi, false)
+			n2, err := NodeFromFileInfo(nodePath, fi)
 			rtest.OK(t, err)
-			n3, err := NodeFromFileInfo(nodePath, fi, true)
-			rtest.OK(t, err)
-			rtest.Assert(t, n2.Equals(*n3), "unexpected node info mismatch %v", cmp.Diff(n2, n3))
 
 			rtest.Assert(t, test.Name == n2.Name,
 				"%v: name doesn't match (%v != %v)", test.Type, test.Name, n2.Name)
diff --git a/internal/restic/node_unix_test.go b/internal/restic/node_unix_test.go
index 9ea7b1725..374326bf7 100644
--- a/internal/restic/node_unix_test.go
+++ b/internal/restic/node_unix_test.go
@@ -128,7 +128,7 @@ func TestNodeFromFileInfo(t *testing.T) {
 				return
 			}
 
-			node, err := NodeFromFileInfo(test.filename, fi, false)
+			node, err := NodeFromFileInfo(test.filename, fi)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/internal/restic/node_windows_test.go b/internal/restic/node_windows_test.go
index 5a5a0a61c..501d5a98a 100644
--- a/internal/restic/node_windows_test.go
+++ b/internal/restic/node_windows_test.go
@@ -165,7 +165,7 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode Node, warningExpec
 	fi, err := os.Lstat(testPath)
 	test.OK(t, errors.Wrapf(err, "Could not Lstat for path: %s", testPath))
 
-	nodeFromFileInfo, err := NodeFromFileInfo(testPath, fi, false)
+	nodeFromFileInfo, err := NodeFromFileInfo(testPath, fi)
 	test.OK(t, errors.Wrapf(err, "Could not get NodeFromFileInfo for path: %s", testPath))
 
 	return testPath, nodeFromFileInfo
diff --git a/internal/restic/node_xattr_test.go b/internal/restic/node_xattr_test.go
deleted file mode 100644
index 5ce77bd28..000000000
--- a/internal/restic/node_xattr_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-//go:build darwin || freebsd || linux || solaris
-// +build darwin freebsd linux solaris
-
-package restic
-
-import (
-	"os"
-	"testing"
-
-	"github.com/pkg/xattr"
-	rtest "github.com/restic/restic/internal/test"
-)
-
-func TestIsListxattrPermissionError(t *testing.T) {
-	xerr := &xattr.Error{
-		Op:   "xattr.list",
-		Name: "test",
-		Err:  os.ErrPermission,
-	}
-	err := handleXattrErr(xerr)
-	rtest.Assert(t, err != nil, "missing error")
-	rtest.Assert(t, IsListxattrPermissionError(err), "expected IsListxattrPermissionError to return true for %v", err)
-
-	xerr.Err = os.ErrNotExist
-	err = handleXattrErr(xerr)
-	rtest.Assert(t, err != nil, "missing error")
-	rtest.Assert(t, !IsListxattrPermissionError(err), "expected IsListxattrPermissionError to return false for %v", err)
-}
diff --git a/internal/restic/tree_test.go b/internal/restic/tree_test.go
index 67ecec897..da674eb1c 100644
--- a/internal/restic/tree_test.go
+++ b/internal/restic/tree_test.go
@@ -86,7 +86,7 @@ func TestNodeComparison(t *testing.T) {
 	fi, err := os.Lstat("tree_test.go")
 	rtest.OK(t, err)
 
-	node, err := restic.NodeFromFileInfo("tree_test.go", fi, false)
+	node, err := restic.NodeFromFileInfo("tree_test.go", fi)
 	rtest.OK(t, err)
 
 	n2 := *node
@@ -127,7 +127,7 @@ func TestTreeEqualSerialization(t *testing.T) {
 		for _, fn := range files[:i] {
 			fi, err := os.Lstat(fn)
 			rtest.OK(t, err)
-			node, err := restic.NodeFromFileInfo(fn, fi, false)
+			node, err := restic.NodeFromFileInfo(fn, fi)
 			rtest.OK(t, err)
 
 			rtest.OK(t, tree.Insert(node))
diff --git a/internal/test/helpers.go b/internal/test/helpers.go
index 3387d36df..242da6079 100644
--- a/internal/test/helpers.go
+++ b/internal/test/helpers.go
@@ -5,7 +5,6 @@ import (
 	"compress/gzip"
 	"fmt"
 	"io"
-	"math/rand"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -13,6 +12,8 @@ import (
 	"testing"
 
 	"github.com/restic/restic/internal/errors"
+
+	mrand "math/rand"
 )
 
 // Assert fails the test if the condition is false.
@@ -70,7 +71,7 @@ func Equals(tb testing.TB, exp, act interface{}, msgs ...string) {
 func Random(seed, count int) []byte {
 	p := make([]byte, count)
 
-	rnd := rand.New(rand.NewSource(int64(seed)))
+	rnd := mrand.New(mrand.NewSource(int64(seed)))
 
 	for i := 0; i < len(p); i += 8 {
 		val := rnd.Int63()