diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 5e9713618..e4c264fba 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -26,10 +26,10 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Log in to the Container registry
- uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
+ uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 4da17a047..790a2d2a4 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -57,7 +57,7 @@ jobs:
steps:
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Set up Go ${{ matrix.go }}
uses: actions/setup-go@v5
@@ -220,7 +220,7 @@ jobs:
steps:
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Set up Go ${{ env.latest_go }}
uses: actions/setup-go@v5
@@ -242,7 +242,7 @@ jobs:
checks: write
steps:
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Set up Go ${{ env.latest_go }}
uses: actions/setup-go@v5
@@ -287,7 +287,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Docker meta
id: meta
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32d6ffa6e..f797834aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,7 +54,7 @@ restic users. The changes are ordered by importance.
* Fix #5249: Fix creation of oversized index by `repair index --read-all-packs`
* Fix #5259: Fix rare crash in command output
* Chg #4938: Update dependencies and require Go 1.23 or newer
- * Chg #5162: Promote feature flags
+ * Chg #5162: Graduate feature flags
* Enh #1378: Add JSON support to `check` command
* Enh #2511: Support generating shell completions to stdout
* Enh #3697: Allow excluding online-only cloud files (e.g. OneDrive)
@@ -76,7 +76,7 @@ restic users. The changes are ordered by importance.
* Enh #5173: Add experimental S3 cold storage support
* Enh #5174: Add xattr support for NetBSD 10+
* Enh #5251: Improve retry handling for flaky `rclone` backends
- * Enh #52897: Make `recover` automatically rebuild index when needed
+ * Enh #5287: Make `recover` automatically rebuild index when needed
## Details
@@ -208,7 +208,7 @@ restic users. The changes are ordered by importance.
https://github.com/restic/restic/pull/4938
- * Change #5162: Promote feature flags
+ * Change #5162: Graduate feature flags
The `deprecate-legacy-index`, `deprecate-s3-legacy-layout`,
`explicit-s3-anonymous-auth` and `safe-forget-keep-tags` features are now stable
@@ -408,13 +408,13 @@ restic users. The changes are ordered by importance.
https://github.com/restic/restic/pull/5251
- * Enhancement #52897: Make `recover` automatically rebuild index when needed
+ * Enhancement #5287: Make `recover` automatically rebuild index when needed
When trying to recover data from an interrupted snapshot, it was previously
necessary to manually run `repair index` before runnning `recover`. This now
happens automatically so that only `recover` is necessary.
- https://github.com/restic/restic/issues/52897
+ https://github.com/restic/restic/issues/5287
https://github.com/restic/restic/pull/5296
diff --git a/changelog/0.18.0_2025-03-27/issue-5287 b/changelog/0.18.0_2025-03-27/issue-5287
index ba5499ead..3d25126f1 100644
--- a/changelog/0.18.0_2025-03-27/issue-5287
+++ b/changelog/0.18.0_2025-03-27/issue-5287
@@ -4,5 +4,5 @@ When trying to recover data from an interrupted snapshot, it was previously
necessary to manually run `repair index` before runnning `recover`. This now
happens automatically so that only `recover` is necessary.
-https://github.com/restic/restic/issues/52897
+https://github.com/restic/restic/issues/5287
https://github.com/restic/restic/pull/5296
diff --git a/changelog/0.18.0_2025-03-27/pull-5162 b/changelog/0.18.0_2025-03-27/pull-5162
index 6a0d75547..8efe8f66d 100644
--- a/changelog/0.18.0_2025-03-27/pull-5162
+++ b/changelog/0.18.0_2025-03-27/pull-5162
@@ -1,4 +1,4 @@
-Change: Promote feature flags
+Change: Graduate feature flags
The `deprecate-legacy-index`, `deprecate-s3-legacy-layout`,
`explicit-s3-anonymous-auth` and `safe-forget-keep-tags` features are
diff --git a/changelog/unreleased/issue-5324 b/changelog/unreleased/issue-5324
new file mode 100644
index 000000000..0f5ff77e4
--- /dev/null
+++ b/changelog/unreleased/issue-5324
@@ -0,0 +1,14 @@
+Bugfix: Correctly handle `backup --stdin-filename` with directories
+
+In restic 0.18.0, the `backup` command failed if a filename that includes
+a least a directory was passed to `--stdin-filename`. For example,
+`--stdin-filename /foo/bar` resulted in the following error:
+
+```
+Fatal: unable to save snapshot: open /foo: no such file or directory
+```
+
+This has been fixed now.
+
+https://github.com/restic/restic/issues/5324
+https://github.com/restic/restic/pull/5356
diff --git a/changelog/unreleased/issue-5325 b/changelog/unreleased/issue-5325
new file mode 100644
index 000000000..883391a0a
--- /dev/null
+++ b/changelog/unreleased/issue-5325
@@ -0,0 +1,7 @@
+Bugfix: Correctly handle `RESTIC_HOST` in `forget` command
+
+The `forget` command did not use the host name from the `RESTIC_HOST`
+environment variable. This has been fixed.
+
+https://github.com/restic/restic/issues/5325
+https://github.com/restic/restic/pull/5327
diff --git a/changelog/unreleased/issue-5342 b/changelog/unreleased/issue-5342
new file mode 100644
index 000000000..e9887fda2
--- /dev/null
+++ b/changelog/unreleased/issue-5342
@@ -0,0 +1,7 @@
+Bugfix: Ignore "chmod not supported" errors when writing files
+
+Restic 0.18.0 introduced a bug that caused "chmod xxx: operation not supported"
+errors to appear when writing to a local file repository that did not support
+chmod (like CIFS or WebDAV mounted via FUSE). Restic now ignores those errors.
+
+https://github.com/restic/restic/issues/5342
diff --git a/changelog/unreleased/issue-5344 b/changelog/unreleased/issue-5344
new file mode 100644
index 000000000..38119d0ec
--- /dev/null
+++ b/changelog/unreleased/issue-5344
@@ -0,0 +1,7 @@
+Bugfix: Ignore EOPNOTSUPP as an error for xattr
+
+Restic 0.18.0 added xattr support for NetBSD 10+, but not all NetBSD
+filesystems support xattrs. Other BSD systems can likewise return
+EOPNOTSUPP, so restic now simply ignores EOPNOTSUPP errors for xattrs.
+
+https://github.com/restic/restic/issues/5344
diff --git a/changelog/unreleased/issue-5429 b/changelog/unreleased/issue-5429
new file mode 100644
index 000000000..e8e7ca513
--- /dev/null
+++ b/changelog/unreleased/issue-5429
@@ -0,0 +1,8 @@
+Bugfix: do not retry if rest-server runs out of space
+
+Rest-server return error `507 Insufficient Storage` if no more storage
+capacity is available at the server. Restic now no longer retries uploads
+in this case.
+
+https://github.com/restic/restic/issues/5429
+https://github.com/restic/restic/pull/5452
diff --git a/changelog/unreleased/pull-5421 b/changelog/unreleased/pull-5421
new file mode 100644
index 000000000..3a76d4fcd
--- /dev/null
+++ b/changelog/unreleased/pull-5421
@@ -0,0 +1,8 @@
+Bugfix: Fix rare crash if directory is removed during backup
+
+In restic 0.18.0, the `backup` command could crash if a directory is removed
+inbetween reading its metadata and listing its directory content.
+
+This has been fixed.
+
+https://github.com/restic/restic/pull/5421
diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go
index 70c0d2fb9..acb5500fd 100644
--- a/cmd/restic/cmd_backup.go
+++ b/cmd/restic/cmd_backup.go
@@ -591,11 +591,12 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
return err
}
}
- targetFS = &fs.Reader{
- ModTime: timeStamp,
- Name: filename,
- Mode: 0644,
- ReadCloser: source,
+ targetFS, err = fs.NewReader(filename, source, fs.ReaderOptions{
+ ModTime: timeStamp,
+ Mode: 0644,
+ })
+ if err != nil {
+ return fmt.Errorf("failed to backup from stdin: %w", err)
}
targets = []string{filename}
}
diff --git a/cmd/restic/cmd_backup_integration_test.go b/cmd/restic/cmd_backup_integration_test.go
index 06d71e345..0002b207f 100644
--- a/cmd/restic/cmd_backup_integration_test.go
+++ b/cmd/restic/cmd_backup_integration_test.go
@@ -632,12 +632,15 @@ func TestStdinFromCommand(t *testing.T) {
testSetupBackupData(t, env)
opts := BackupOptions{
- StdinCommand: true,
- StdinFilename: "stdin",
+ StdinCommand: true,
+ // test that subdirectories are handled correctly
+ StdinFilename: "stdin/subdir/file",
}
testRunBackup(t, filepath.Dir(env.testdata), []string{"python", "-c", "import sys; print('something'); sys.exit(0)"}, opts, env.gopts)
- testListSnapshots(t, env.gopts, 1)
+ snapshots := testListSnapshots(t, env.gopts, 1)
+ files := testRunLs(t, env.gopts, snapshots[0].String())
+ rtest.Assert(t, includes(files, "/stdin/subdir/file"), "file %q missing from snapshot, got %v", "stdin/subdir/file", files)
testRunCheck(t, env.gopts)
}
diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go
index 2bfacc4de..3a410e223 100644
--- a/cmd/restic/cmd_forget.go
+++ b/cmd/restic/cmd_forget.go
@@ -137,13 +137,14 @@ func (opts *ForgetOptions) AddFlags(f *pflag.FlagSet) {
f.Var(&opts.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
f.BoolVar(&opts.UnsafeAllowRemoveAll, "unsafe-allow-remove-all", false, "allow deleting all snapshots of a snapshot group")
- initMultiSnapshotFilter(f, &opts.SnapshotFilter, false)
f.StringArrayVar(&opts.Hosts, "hostname", nil, "only consider snapshots with the given `hostname` (can be specified multiple times)")
err := f.MarkDeprecated("hostname", "use --host")
if err != nil {
// MarkDeprecated only returns an error when the flag is not found
panic(err)
}
+ // must be defined after `--hostname` to not override the default value from the environment
+ initMultiSnapshotFilter(f, &opts.SnapshotFilter, false)
f.BoolVarP(&opts.Compact, "compact", "c", false, "use compact output format")
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true}
diff --git a/cmd/restic/cmd_forget_test.go b/cmd/restic/cmd_forget_test.go
index ddeef028a..0d60f7445 100644
--- a/cmd/restic/cmd_forget_test.go
+++ b/cmd/restic/cmd_forget_test.go
@@ -5,6 +5,7 @@ import (
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
+ "github.com/spf13/pflag"
)
func TestForgetPolicyValues(t *testing.T) {
@@ -92,3 +93,10 @@ func TestForgetOptionValues(t *testing.T) {
}
}
}
+
+func TestForgetHostnameDefaulting(t *testing.T) {
+ t.Setenv("RESTIC_HOST", "testhost")
+ opts := ForgetOptions{}
+ opts.AddFlags(pflag.NewFlagSet("test", pflag.ContinueOnError))
+ rtest.Equals(t, []string{"testhost"}, opts.Hosts)
+}
diff --git a/cmd/restic/global_debug.go b/cmd/restic/global_debug.go
index 1fe35146a..e536bffea 100644
--- a/cmd/restic/global_debug.go
+++ b/cmd/restic/global_debug.go
@@ -30,14 +30,12 @@ func registerProfiling(cmd *cobra.Command) {
return profiler.Start(profiler.opts)
}
- origPostRun := cmd.PersistentPostRunE
- cmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error {
+ // Once https://github.com/spf13/cobra/issues/1893 is fixed,
+ // this could use PersistentPostRunE instead of OnFinalize,
+ // reverting https://github.com/restic/restic/pull/5373.
+ cobra.OnFinalize(func() {
profiler.Stop()
- if origPostRun != nil {
- return origPostRun(cmd, args)
- }
- return nil
- }
+ })
profiler.opts.AddFlags(cmd.PersistentFlags())
}
diff --git a/doc/020_installation.rst b/doc/020_installation.rst
index a39ca896b..c6f368f21 100644
--- a/doc/020_installation.rst
+++ b/doc/020_installation.rst
@@ -74,6 +74,16 @@ avoid any conflicts:
$ dnf copr remove copart/restic
+Gentoo Linux
+============
+
+On `Gentoo Linux `__, you can install the ``restic``
+package from the official repos using ``emerge``:
+
+.. code-block:: console
+
+ # emerge restic
+
macOS
=====
@@ -175,15 +185,19 @@ restic can be installed from the official repo of Solus via the ``eopkg`` packag
Windows
=======
-restic can be installed using `Scoop `__:
+restic can be installed using either `Scoop `__ or `WinGet `__.
+
+Regardless of the method, the ``restic.exe`` binary will be added to your ``PATH`` automatically, making the ``restic`` command accessible in Powershell or CMD.
.. code-block:: console
scoop install restic
-Using this installation method, ``restic.exe`` will automatically be available
-in the ``PATH``. It can be called from cmd.exe or PowerShell by typing ``restic``.
+.. code-block:: console
+ winget install --exact --id restic.restic --scope Machine
+
+By default, WinGet will install restic into the ``User`` scope, which is typically in your user's ``%LOCALAPPDATA%`` directory. This behavior may be undesirable for system-wide backups, so specifying ``--scope Machine`` is recommended so that restic is installed into ``%ProgramFiles%``. This requires elevation.
.. _official_binaries:
@@ -253,13 +267,6 @@ the `restic beta download site
and ready to run, and a new version is built every time a push is made to the
master branch.
-Windows
-=======
-
-On Windows, put the `restic.exe` binary into `%SystemRoot%\\System32` to use restic
-in scripts without the need for absolute paths to the binary. This requires
-administrator rights.
-
Docker Container
****************
diff --git a/doc/030_preparing_a_new_repo.rst b/doc/030_preparing_a_new_repo.rst
index 34a0cd8bc..fb7963739 100644
--- a/doc/030_preparing_a_new_repo.rst
+++ b/doc/030_preparing_a_new_repo.rst
@@ -349,7 +349,7 @@ Wasabi
S3 storage from `Wasabi `__ can be used as follows.
-- Determine the correct Wasabi service URL for your bucket `here `__.
+- Determine the correct Wasabi service URL for your bucket `here `__.
- Set environment variables with the necessary account credentials
.. code-block:: console
@@ -854,6 +854,6 @@ with an empty password, use the following command.
restic init --insecure-no-password
-The ``init`` and ``copy`` command also support the option ``--from-insecure-no-password``
+The ``init`` and ``copy`` commands also support the option ``--from-insecure-no-password``
which applies to the source repository. The ``key add`` and ``key passwd`` commands
-include the ``--new-insecure-no-password`` option to add or set and empty password.
+include the ``--new-insecure-no-password`` option to add or set an empty password.
diff --git a/doc/040_backup.rst b/doc/040_backup.rst
index a30d80402..a2092e486 100644
--- a/doc/040_backup.rst
+++ b/doc/040_backup.rst
@@ -346,9 +346,10 @@ A trailing ``/`` is ignored, a leading ``/`` anchors the pattern at the root dir
This means, ``/bin`` matches ``/bin/bash`` but does not match ``/usr/bin/restic``.
Regular wildcards cannot be used to match over the directory separator ``/``,
-e.g. ``b*ash`` matches ``/bin/bash`` but does not match ``/bin/ash``. For this,
-the special wildcard ``**`` can be used to match arbitrary sub-directories: The
-pattern ``foo/**/bar`` matches:
+e.g. ``b*ash`` matches ``/bin/bash`` but does not match ``/bin/ash``. To match
+across an arbitrary number of subdirectories, use the special ``**`` wildcard.
+The ``**`` must be positioned between path separators. The pattern
+``foo/**/bar`` matches:
* ``/dir1/foo/dir2/bar/file``
* ``/foo/bar/file``
diff --git a/doc/045_working_with_repos.rst b/doc/045_working_with_repos.rst
index d5f2240b8..5fadac637 100644
--- a/doc/045_working_with_repos.rst
+++ b/doc/045_working_with_repos.rst
@@ -297,7 +297,7 @@ Note that it is not possible to change the chunker parameters of an existing rep
Removing files from snapshots
=============================
-Snapshots sometimes turn out to include more files that intended. Instead of
+Snapshots sometimes turn out to include more files than intended. Instead of
removing the snapshots entirely and running the corresponding backup commands
again (which is not always practical after the fact) it is possible to remove
the unwanted files from affected snapshots by rewriting them using the
@@ -338,8 +338,8 @@ the only fields added are ``TotalFilesProcessed`` and ``TotalBytesProcessed``.
By default, the ``rewrite`` command will keep the original snapshots and create
new ones for every snapshot which was modified during rewriting. The new
-snapshots are marked with the tag ``rewrite`` to differentiate them from the
-original, rewritten snapshots.
+snapshots are marked with the tag ``rewrite`` to distinguish them from the
+original, untouched snapshots.
Alternatively, you can use the ``--forget`` option to immediately remove the
original snapshots. In this case, no tag is added to the new snapshots. Please
diff --git a/doc/047_tuning_backup_parameters.rst b/doc/047_tuning_backup_parameters.rst
index 650f111be..670828239 100644
--- a/doc/047_tuning_backup_parameters.rst
+++ b/doc/047_tuning_backup_parameters.rst
@@ -34,11 +34,12 @@ Backend Connections
Restic uses a global limit for the number of concurrent connections to a backend.
This limit can be configured using ``-o .connections=5``, for example for
-the REST backend the parameter would be ``-o rest.connections=5``. By default restic uses
-``5`` connections for each backend, except for the local backend which uses a limit of ``2``.
-The defaults should work well in most cases. For high-latency backends it can be beneficial
-to increase the number of connections. Please be aware that this increases the resource
-consumption of restic and that a too high connection count *will degrade performance*.
+the REST backend the parameter would be ``-o rest.connections=5`` or for the local backend
+``-o local.connections=2``. By default restic uses ``5`` connections for each backend,
+except for the local backend which uses a limit of ``2``. The defaults should work well in
+most cases. For high-latency backends it can be beneficial to increase the number of
+connections. Please be aware that this increases the resource consumption of restic and
+that a too high connection count *will degrade performance*.
CPU Usage
diff --git a/doc/090_participating.rst b/doc/090_participating.rst
index 890bd9018..a035af7ca 100644
--- a/doc/090_participating.rst
+++ b/doc/090_participating.rst
@@ -71,8 +71,17 @@ The program can be built with debug support like this:
$ go run build.go -tags debug
This will make the ``restic debug `` available which can be used to
-inspect internal data structures. In addition, this enables profiling support
-which can help with investigation performance and memory usage issues.
+inspect internal data structures.
+
+In addition, this enables profiling flags such as ``--cpu-profile`` and
+``--mem-profile`` which can help with investigation performance and memory usage
+issues. See ``restic help`` for more details and a few additional
+``--...-profile`` flags.
+
+Running Restic with profiling enabled generates a ``.pprof`` file such as
+``cpu.pprof``. To view a profile in a web browser, first make sure that the
+``dot`` command from `Graphviz `__ is in the PATH. Then,
+run ``go tool pprof -http : cpu.pprof``.
************
diff --git a/doc/developer_information.rst b/doc/developer_information.rst
index a2d7adfe1..ce28317c6 100644
--- a/doc/developer_information.rst
+++ b/doc/developer_information.rst
@@ -18,7 +18,7 @@ depends on the following things:
* The path to the Go workspace (``GOPATH=/home/build/go``)
* Other environment variables (mostly ``$GOOS``, ``$GOARCH``, ``$CGO_ENABLED``)
-In addition, The compressed ZIP files for Windows depends on the modification
+In addition, the compressed ZIP files for Windows depends on the modification
timestamp and filename of the binary contained in it. In order to reproduce the
exact same ZIP file every time, we update the timestamp of the file ``VERSION``
in the source code archive and set the timezone to Europe/Berlin.
@@ -52,10 +52,10 @@ In the following example, we'll use the file ``restic-0.14.0.tar.gz`` and Go
$ go version
go version go1.19 linux/amd64
- $ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate -o restic_linux_amd64 ./cmd/restic
+ $ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate,disable_grpc_modules -o restic_linux_amd64 ./cmd/restic
$ bzip2 restic_linux_amd64
- $ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate -o restic_0.14.0_windows_amd64.exe ./cmd/restic
+ $ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate,disable_grpc_modules -o restic_0.14.0_windows_amd64.exe ./cmd/restic
$ touch --reference VERSION restic_0.14.0_windows_amd64.exe
$ TZ=Europe/Berlin zip -q -X restic_0.14.0_windows_amd64.zip restic_0.14.0_windows_amd64.exe
@@ -66,7 +66,7 @@ The released binaries for restic are built using a Docker container. You can
find it on `Docker Hub `__ as
``restic/builder``, the ``Dockerfile`` and instructions on how to build the
container can be found in the `GitHub repository
-`__
+`__.
The container serves the following goals:
* Have a very controlled environment which is independent from the local system
@@ -93,7 +93,7 @@ The following steps are necessary to build the binaries:
mkdir output
-3. Mount the source code and the output directory in the container and run the default command, which starts ``helpers/build-release-binaries/main.go``:
+4. Mount the source code and the output directory in the container and run the default command, which starts ``helpers/build-release-binaries/main.go``:
.. code::
@@ -103,7 +103,7 @@ The following steps are necessary to build the binaries:
restic/builder \
go run helpers/build-release-binaries/main.go --version 0.14.0
-4. If anything goes wrong, you can enable debug output like this:
+5. If anything goes wrong, you can enable debug output like this:
.. code::
@@ -114,7 +114,7 @@ The following steps are necessary to build the binaries:
go run helpers/build-release-binaries/main.go --version 0.14.0 --verbose
Verifying SLSA Provenance for GHCR Docker Images
-*******************************************
+************************************************
Our Docker images in the GitHub Container Registry (GHCR) are built with SLSA
(Supply-chain Levels for Software Artifacts) provenance.
@@ -131,9 +131,8 @@ To verify this provenance:
--source-uri github.com/restic/restic \
@
- Replace `` with the Git tag of the release you're verifying, ``
- with the full name of the Docker image (including the registry), and ``
- with the SHA256 digest of the image.
+ Replace `` with the full name of the Docker image (including the registry),
+ and `` with the SHA256 digest of the image.
3. If the verification is successful, you'll see output indicating that the provenance
is valid.
@@ -146,7 +145,7 @@ Verifying the Official Binaries
To verify the official binaries, you can either build them yourself using the above
instructions or use the ``helpers/verify-release-binaries.sh`` script from the restic
-repository. Run it as ``helpers/verify-release-binaries.sh restic_version go_version``.
+repository. Run it as ``helpers/verify-release-binaries.sh $restic_version $go_version``.
The specified go compiler version must match the one used to build the official
binaries. For example, for restic 0.16.2 the command would be
``helpers/verify-release-binaries.sh 0.16.2 1.21.3``.
diff --git a/doc/faq.rst b/doc/faq.rst
index 3b62f641d..a235ea916 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -110,9 +110,8 @@ How can I specify encryption passwords automatically?
When you run ``restic backup``, you need to enter the passphrase on
the console. This is not very convenient for automated backups, so you
can also provide the password through the ``--password-file`` option, or one of
-the environment variables ``RESTIC_PASSWORD`` or ``RESTIC_PASSWORD_FILE``.
-A discussion is in progress over implementing unattended backups happens in
-:issue:`533`.
+the environment variables: ``RESTIC_PASSWORD``, ``RESTIC_PASSWORD_FILE``,
+or ``RESTIC_PASSWORD_COMMAND``.
.. important:: Be careful how you set the environment; using the env
command, a `system()` call or using inline shell
@@ -124,10 +123,33 @@ A discussion is in progress over implementing unattended backups happens in
`accessible only to that user`_. Please make sure that
the permissions on the files where the password is
eventually stored are safe (e.g. `0600` and owned by
- root).
+ root). Note also that ``RESTIC_PASSWORD_COMMAND`` is
+ safe because it does not export the password itself to
+ the environment.
.. _accessible only to that user: https://security.stackexchange.com/questions/14000/environment-variable-accessibility-in-linux/14009#14009
+On platforms with an available keychain, keyring or similar secret store, a
+user can add and then dynamically retrieve passwords, cloud credentials,
+repository paths, or any other data deemed sensitive. Here's an example of
+part of a shell script using the `built-in`_ ``security`` command on macOS
+to retrieve credentials from the system's Keychain before running various
+``restic`` commands:
+
+.. _built-in: https://ss64.com/mac/security.html
+
+::
+
+ export GOOGLE_PROJECT_ID=$(security find-generic-password -a resticGCS -s restic_project_ID -w)
+
+ export GOOGLE_APPLICATION_CREDENTIALS=$(security find-generic-password -a resticGCS -s restic_key -w)
+
+ export RESTIC_REPOSITORY=$(security find-generic-password -a resticGCS -s restic_repo_path -w)
+
+ export RESTIC_PASSWORD_COMMAND='security find-generic-password -a resticGCS -s restic_pwd -w'
+
+
+
How to prioritize restic's IO and CPU time
------------------------------------------
@@ -253,7 +275,7 @@ Archive** storage classes is available:
.. code-block:: console
$ restic backup -o s3.storage-class=GLACIER somedir/
- $ RESTIC_FEATURES=s3-restore restic restore -o s3.enable-restore=1 -o s3.restore-days=7 -o s3.restore-timeout=1d latest
+ $ RESTIC_FEATURES=s3-restore restic restore -o s3.enable-restore=1 -o s3.restore-days=7 -o s3.restore-timeout=24h latest
**Notes:**
diff --git a/go.mod b/go.mod
index 1e1ba52f2..65eba8d20 100644
--- a/go.mod
+++ b/go.mod
@@ -8,9 +8,9 @@ godebug winsymlink=0
require (
cloud.google.com/go/storage v1.51.0
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
- github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
+ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
github.com/Backblaze/blazer v0.7.2
github.com/Microsoft/go-winio v0.6.2
github.com/anacrolix/fuse v0.3.1
@@ -21,42 +21,42 @@ require (
github.com/google/go-cmp v0.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/klauspost/compress v1.18.0
- github.com/minio/minio-go/v7 v7.0.88
+ github.com/minio/minio-go/v7 v7.0.89
github.com/ncw/swift/v2 v2.0.3
- github.com/peterbourgon/unixtransport v0.0.4
+ github.com/peterbourgon/unixtransport v0.0.6
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0
- github.com/pkg/sftp v1.13.8
+ github.com/pkg/sftp v1.13.9
github.com/pkg/xattr v0.4.10
github.com/restic/chunker v0.4.0
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
go.uber.org/automaxprocs v1.6.0
- golang.org/x/crypto v0.36.0
- golang.org/x/net v0.37.0
- golang.org/x/oauth2 v0.28.0
- golang.org/x/sync v0.12.0
- golang.org/x/sys v0.31.0
- golang.org/x/term v0.30.0
- golang.org/x/text v0.23.0
- golang.org/x/time v0.11.0
- google.golang.org/api v0.227.0
+ golang.org/x/crypto v0.41.0
+ golang.org/x/net v0.43.0
+ golang.org/x/oauth2 v0.30.0
+ golang.org/x/sync v0.16.0
+ golang.org/x/sys v0.35.0
+ golang.org/x/term v0.34.0
+ golang.org/x/text v0.28.0
+ golang.org/x/time v0.12.0
+ google.golang.org/api v0.248.0
)
require (
- cel.dev/expr v0.19.2 // indirect
- cloud.google.com/go v0.118.3 // indirect
- cloud.google.com/go/auth v0.15.0 // indirect
- cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
- cloud.google.com/go/compute/metadata v0.6.0 // indirect
- cloud.google.com/go/iam v1.4.1 // indirect
- cloud.google.com/go/monitoring v1.24.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
- github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
- github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
+ cel.dev/expr v0.24.0 // indirect
+ cloud.google.com/go v0.120.0 // indirect
+ cloud.google.com/go/auth v0.16.5 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
+ cloud.google.com/go/compute/metadata v0.8.0 // indirect
+ cloud.google.com/go/iam v1.5.2 // indirect
+ cloud.google.com/go/monitoring v1.24.2 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
+ github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
+ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
- github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
+ github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
@@ -64,17 +64,18 @@ require (
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-ini/ini v1.67.0 // indirect
- github.com/go-logr/logr v1.4.2 // indirect
+ github.com/go-jose/go-jose/v4 v4.0.5 // indirect
+ github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.5 // indirect
- github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
+ github.com/golang-jwt/jwt/v5 v5.2.3 // indirect
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
- github.com/googleapis/gax-go/v2 v2.14.1 // indirect
+ github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/klauspost/cpuid/v2 v2.2.9 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/minio/crc64nvme v1.0.1 // indirect
@@ -83,19 +84,21 @@ require (
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
+ github.com/zeebo/errs v1.4.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
- go.opentelemetry.io/otel v1.34.0 // indirect
- go.opentelemetry.io/otel/metric v1.34.0 // indirect
- go.opentelemetry.io/otel/sdk v1.34.0 // indirect
- go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
- go.opentelemetry.io/otel/trace v1.34.0 // indirect
- google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
- google.golang.org/grpc v1.71.0 // indirect
- google.golang.org/protobuf v1.36.5 // indirect
+ go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
+ go.opentelemetry.io/otel v1.36.0 // indirect
+ go.opentelemetry.io/otel/metric v1.36.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.36.0 // indirect
+ go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
+ go.opentelemetry.io/otel/trace v1.36.0 // indirect
+ google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
+ google.golang.org/grpc v1.74.2 // indirect
+ google.golang.org/protobuf v1.36.7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 54770e377..ad1804f6f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,45 +1,45 @@
-cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
-cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
-cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
-cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
-cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
-cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
-cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
-cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
-cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
-cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
-cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
-cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM=
+cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
+cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
+cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
+cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
+cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
+cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
+cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
+cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
+cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
+cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
+cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
+cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
-cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
-cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
-cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM=
-cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc=
+cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
+cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
+cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
+cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
cloud.google.com/go/storage v1.51.0 h1:ZVZ11zCiD7b3k+cH5lQs/qcNaoSz3U9I0jgwVzqDlCw=
cloud.google.com/go/storage v1.51.0/go.mod h1:YEJfu/Ki3i5oHC/7jyTgsGZwdQ8P9hqMqvpi5kRKGgc=
-cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE=
-cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
+cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
+cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 h1:FwladfywkNirM+FZYLBR2kBz5C8Tg0fw5w5Y7meRXWI=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2/go.mod h1:vv5Ad0RrIoT1lJFdWBZwt4mB1+j+V8DUroixmKDTCdk=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/Backblaze/blazer v0.7.2 h1:UWNHMLB+Nf+UmbO2qkVvgriODLEMz4kIyr2Hm+DVXQM=
github.com/Backblaze/blazer v0.7.2/go.mod h1:T4y3EYa9IQ5J0PKc/C/J8/CEnSd3qa/lgNw938wZg10=
-github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
-github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
@@ -65,8 +65,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
-github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
+github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
+github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -96,17 +96,19 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
+github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
-github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
-github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
+github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -124,20 +126,20 @@ 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.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
-github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
-github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
+github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
+github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
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=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
-github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
+github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
+github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
-github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -155,15 +157,15 @@ github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.88 h1:v8MoIJjwYxOkehp+eiLIuvXk87P2raUtoU5klrAAshs=
-github.com/minio/minio-go/v7 v7.0.88/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg=
+github.com/minio/minio-go/v7 v7.0.89 h1:hx4xV5wwTUfyv8LarhJAwNecnXpoTsj9v3f3q/ZkiJU=
+github.com/minio/minio-go/v7 v7.0.89/go.mod h1:2rFnGAp02p7Dddo1Fq4S2wYOfpF0MUTSeLTRC90I204=
github.com/ncw/swift/v2 v2.0.3 h1:8R9dmgFIWs+RiVlisCEfiQiik1hjuR0JnOkLxaP9ihg=
github.com/ncw/swift/v2 v2.0.3/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
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/peterbourgon/unixtransport v0.0.6 h1:sWIViDoRIg2MeyRbWTUDdvvY5XbKaaSr9ionmzNGXWY=
+github.com/peterbourgon/unixtransport v0.0.6/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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -171,8 +173,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
-github.com/pkg/sftp v1.13.8 h1:Xt7eJ/xqXv7s0VuzFw7JXhZj6Oc1zI6l4GK8KP9sFB0=
-github.com/pkg/sftp v1.13.8/go.mod h1:DmvEkvKE2lshEeuo2JMp06yqcx9HVnR7e3zqQl42F3U=
+github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
+github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
github.com/pkg/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA=
github.com/pkg/xattr v0.4.10/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
@@ -181,8 +183,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
-github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
-github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
+github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
+github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/restic/chunker v0.4.0 h1:YUPYCUn70MYP7VO4yllypp2SjmsRhRJaad3xKu1QFRw=
github.com/restic/chunker v0.4.0/go.mod h1:z0cH2BejpW636LXw0R/BGyv+Ey8+m9QGiOanDHItzyw=
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
@@ -199,6 +201,8 @@ github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
+github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -214,26 +218,28 @@ github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDH
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
+github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao=
-go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
-go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
-go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw=
+go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
+go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
+go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
-go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
-go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
-go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
-go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
-go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
-go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
-go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
-go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
+go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
+go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
+go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
+go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
+go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
+go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
+go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
+go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -243,8 +249,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
-golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
-golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
+golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
@@ -266,10 +272,10 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
-golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
-golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
-golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
-golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
+golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
+golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
+golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -278,8 +284,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
-golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -299,8 +305,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
+golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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=
@@ -311,8 +317,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
+golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -324,10 +330,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
-golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
-golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
-golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
-golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
+golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
@@ -339,18 +345,18 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc=
-google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ=
-google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
-google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
-google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
-google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
-google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
-google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
-google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
-google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y=
+google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k=
+google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
+google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
+google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
+google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
+google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
+google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
+google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go
index 4a6577d27..981b1fecc 100644
--- a/internal/archiver/archiver.go
+++ b/internal/archiver/archiver.go
@@ -264,6 +264,11 @@ 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, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) {
node, err := meta.ToNode(ignoreXattrListError)
+ // node does not exist. This prevents all further processing for this file.
+ // If an error and a node are returned, then preserve as much data as possible (see below).
+ if err != nil && node == nil {
+ return nil, err
+ }
if !arch.WithAtime {
node.AccessTime = node.ModTime
}
@@ -718,8 +723,14 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree,
arch.trackItem(snItem, oldNode, n, is, time.Since(start))
})
if err != nil {
+ err = arch.error(join(snPath, name), err)
+ if err == nil {
+ // ignore error
+ continue
+ }
return futureNode{}, 0, err
}
+
nodes = append(nodes, fn)
}
diff --git a/internal/archiver/archiver_test.go b/internal/archiver/archiver_test.go
index 9a25f7fad..098e13395 100644
--- a/internal/archiver/archiver_test.go
+++ b/internal/archiver/archiver_test.go
@@ -174,12 +174,11 @@ func TestArchiverSaveFileReaderFS(t *testing.T) {
ts := time.Now()
filename := "xx"
- readerFs := &fs.Reader{
- ModTime: ts,
- Mode: 0123,
- Name: filename,
- ReadCloser: io.NopCloser(strings.NewReader(test.Data)),
- }
+ readerFs, err := fs.NewReader(filename, io.NopCloser(strings.NewReader(test.Data)), fs.ReaderOptions{
+ ModTime: ts,
+ Mode: 0123,
+ })
+ rtest.OK(t, err)
node, stats := saveFile(t, repo, filename, readerFs)
@@ -288,13 +287,11 @@ func TestArchiverSaveReaderFS(t *testing.T) {
ts := time.Now()
filename := "xx"
- readerFs := &fs.Reader{
- ModTime: ts,
- Mode: 0123,
- Name: filename,
- ReadCloser: io.NopCloser(strings.NewReader(test.Data)),
- }
-
+ readerFs, err := fs.NewReader(filename, io.NopCloser(strings.NewReader(test.Data)), fs.ReaderOptions{
+ ModTime: ts,
+ Mode: 0123,
+ })
+ rtest.OK(t, err)
arch := New(repo, readerFs, Options{})
arch.Error = func(item string, err error) error {
t.Errorf("archiver error for %v: %v", item, err)
@@ -1849,8 +1846,8 @@ func TestArchiverParent(t *testing.T) {
func TestArchiverErrorReporting(t *testing.T) {
ignoreErrorForBasename := func(basename string) ErrorFunc {
return func(item string, err error) error {
- if filepath.Base(item) == "targetfile" {
- t.Logf("ignoring error for targetfile: %v", err)
+ if filepath.Base(item) == basename {
+ t.Logf("ignoring error for %v: %v", basename, err)
return nil
}
@@ -1873,12 +1870,13 @@ func TestArchiverErrorReporting(t *testing.T) {
}
var tests = []struct {
- name string
- src TestDir
- want TestDir
- prepare func(t testing.TB)
- errFn ErrorFunc
- mustError bool
+ name string
+ targets []string
+ src TestDir
+ want TestDir
+ prepare func(t testing.TB)
+ errFn ErrorFunc
+ errStr []string
}{
{
name: "no-error",
@@ -1891,8 +1889,8 @@ func TestArchiverErrorReporting(t *testing.T) {
src: TestDir{
"targetfile": TestFile{Content: "foobar"},
},
- prepare: chmodUnreadable("targetfile"),
- mustError: true,
+ prepare: chmodUnreadable("targetfile"),
+ errStr: []string{"open targetfile: permission denied"},
},
{
name: "file-unreadable-ignore-error",
@@ -1913,8 +1911,8 @@ func TestArchiverErrorReporting(t *testing.T) {
"targetfile": TestFile{Content: "foobar"},
},
},
- prepare: chmodUnreadable("subdir/targetfile"),
- mustError: true,
+ prepare: chmodUnreadable("subdir/targetfile"),
+ errStr: []string{"open subdir/targetfile: permission denied"},
},
{
name: "file-subdir-unreadable-ignore-error",
@@ -1932,6 +1930,20 @@ func TestArchiverErrorReporting(t *testing.T) {
prepare: chmodUnreadable("subdir/targetfile"),
errFn: ignoreErrorForBasename("targetfile"),
},
+ {
+ name: "parent-dir-missing",
+ targets: []string{"subdir/missing"},
+ src: TestDir{},
+ errStr: []string{"stat subdir: no such file or directory", "CreateFile subdir: The system cannot find the file specified"},
+ },
+ {
+ name: "parent-dir-missing-filtered",
+ targets: []string{"targetfile", "subdir/missing"},
+ src: TestDir{
+ "targetfile": TestFile{Content: "foobar"},
+ },
+ errFn: ignoreErrorForBasename("subdir"),
+ },
}
for _, test := range tests {
@@ -1951,14 +1963,21 @@ func TestArchiverErrorReporting(t *testing.T) {
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
arch.Error = test.errFn
- _, snapshotID, _, err := arch.Snapshot(ctx, []string{"."}, SnapshotOptions{Time: time.Now()})
- if test.mustError {
- if err != nil {
- t.Logf("found expected error (%v), skipping further checks", err)
- return
+ target := test.targets
+ if len(target) == 0 {
+ target = []string{"."}
+ }
+ _, snapshotID, _, err := arch.Snapshot(ctx, target, SnapshotOptions{Time: time.Now()})
+ if test.errStr != nil {
+ // check if any of the expected errors are contained in the error message
+ for _, errStr := range test.errStr {
+ if strings.Contains(err.Error(), errStr) {
+ t.Logf("found expected error (%v)", err)
+ return
+ }
}
- t.Fatalf("expected error not returned by archiver")
+ t.Fatalf("expected error (%v) not returned by archiver, got (%v)", test.errStr, err)
return
}
diff --git a/internal/backend/local/local_unix.go b/internal/backend/local/local_unix.go
index e52587456..b885ae1a3 100644
--- a/internal/backend/local/local_unix.go
+++ b/internal/backend/local/local_unix.go
@@ -43,5 +43,12 @@ func isMacENOTTY(err error) bool {
// set file to readonly
func setFileReadonly(f string, mode os.FileMode) error {
- return os.Chmod(f, mode&^0222)
+ err := os.Chmod(f, mode&^0222)
+
+ // ignore the error if the FS does not support setting this mode (e.g. CIFS with gvfs on Linux)
+ if err != nil && errors.Is(err, errors.ErrUnsupported) {
+ return nil
+ }
+
+ return err
}
diff --git a/internal/backend/rest/rest.go b/internal/backend/rest/rest.go
index 8df91beb3..5776f284f 100644
--- a/internal/backend/rest/rest.go
+++ b/internal/backend/rest/rest.go
@@ -182,7 +182,7 @@ func (b *Backend) IsPermanentError(err error) bool {
var rerr *restError
if errors.As(err, &rerr) {
- if rerr.StatusCode == http.StatusRequestedRangeNotSatisfiable || rerr.StatusCode == http.StatusUnauthorized || rerr.StatusCode == http.StatusForbidden {
+ if rerr.StatusCode == http.StatusRequestedRangeNotSatisfiable || rerr.StatusCode == http.StatusUnauthorized || rerr.StatusCode == http.StatusForbidden || rerr.StatusCode == http.StatusInsufficientStorage {
return true
}
}
diff --git a/internal/backend/s3/config.go b/internal/backend/s3/config.go
index 77f27408e..365b16bf1 100644
--- a/internal/backend/s3/config.go
+++ b/internal/backend/s3/config.go
@@ -26,7 +26,7 @@ type Config struct {
EnableRestore bool `option:"enable-restore" help:"restore objects from GLACIER or DEEP_ARCHIVE storage classes (default: false, requires \"s3-restore\" feature flag)"`
RestoreDays int `option:"restore-days" help:"lifetime in days of restored object (default: 7)"`
- RestoreTimeout time.Duration `option:"restore-timeout" help:"maximum time to wait for objects transition (default: 1d)"`
+ RestoreTimeout time.Duration `option:"restore-timeout" help:"maximum time to wait for objects transition (default: 24h)"`
RestoreTier string `option:"restore-tier" help:"Retrieval tier at which the restore will be processed. (Standard, Bulk or Expedited) (default: Standard)"`
Connections uint `option:"connections" help:"set a limit for the number of concurrent connections (default: 5)"`
diff --git a/internal/fs/fs_reader.go b/internal/fs/fs_reader.go
index bbe5c95ab..c4e98be0f 100644
--- a/internal/fs/fs_reader.go
+++ b/internal/fs/fs_reader.go
@@ -19,97 +19,135 @@ import (
// be opened once, all subsequent open calls return syscall.EIO. For Lstat(),
// the provided FileInfo is returned.
type Reader struct {
- Name string
- io.ReadCloser
+ items map[string]readerItem
+}
- // for FileInfo
+type ReaderOptions struct {
Mode os.FileMode
ModTime time.Time
Size int64
AllowEmptyFile bool
+}
- open sync.Once
+type readerItem struct {
+ open *sync.Once
+ fi *ExtendedFileInfo
+ rc io.ReadCloser
+ allowEmptyFile bool
+
+ children []string
}
// statically ensure that Local implements FS.
var _ FS = &Reader{}
+func NewReader(name string, r io.ReadCloser, opts ReaderOptions) (*Reader, error) {
+ items := make(map[string]readerItem)
+ name = readerCleanPath(name)
+ if name == "/" {
+ return nil, fmt.Errorf("invalid filename specified")
+ }
+
+ isFile := true
+ for {
+ if isFile {
+ fi := &ExtendedFileInfo{
+ Name: path.Base(name),
+ Mode: opts.Mode,
+ ModTime: opts.ModTime,
+ Size: opts.Size,
+ }
+ items[name] = readerItem{
+ open: &sync.Once{},
+ fi: fi,
+ rc: r,
+ allowEmptyFile: opts.AllowEmptyFile,
+ }
+ isFile = false
+ } else {
+ fi := &ExtendedFileInfo{
+ Name: path.Base(name),
+ Mode: os.ModeDir | 0755,
+ ModTime: opts.ModTime,
+ Size: 0,
+ }
+ items[name] = readerItem{
+ fi: fi,
+ // keep the children set during the previous iteration
+ children: items[name].children,
+ }
+ }
+
+ parent := path.Dir(name)
+ if parent == name {
+ break
+ }
+ // add the current file to the children of the parent directory
+ item := items[parent]
+ item.children = append(item.children, path.Base(name))
+ items[parent] = item
+
+ name = parent
+ }
+ return &Reader{
+ items: items,
+ }, nil
+}
+
+func readerCleanPath(name string) string {
+ return path.Clean("/" + name)
+}
+
// VolumeName returns leading volume name, for the Reader file system it's
// always the empty string.
func (fs *Reader) VolumeName(_ string) string {
return ""
}
-func (fs *Reader) fi() *ExtendedFileInfo {
- return &ExtendedFileInfo{
- Name: fs.Name,
- Mode: fs.Mode,
- ModTime: fs.ModTime,
- Size: fs.Size,
- }
-}
-
func (fs *Reader) OpenFile(name string, flag int, _ bool) (f File, err error) {
if flag & ^(O_RDONLY|O_NOFOLLOW) != 0 {
return nil, pathError("open", name,
fmt.Errorf("invalid combination of flags 0x%x", flag))
}
- switch name {
- case fs.Name:
- fs.open.Do(func() {
- f = newReaderFile(fs.ReadCloser, fs.fi(), fs.AllowEmptyFile)
+ name = readerCleanPath(name)
+ item, ok := fs.items[name]
+ if !ok {
+ return nil, pathError("open", name, syscall.ENOENT)
+ }
+
+ // Check if the path matches our target file
+ if item.rc != nil {
+ item.open.Do(func() {
+ f = newReaderFile(item.rc, item.fi, item.allowEmptyFile)
})
if f == nil {
return nil, pathError("open", name, syscall.EIO)
}
- return f, nil
- case "/", ".":
- f = fakeDir{
- entries: []string{fs.fi().Name},
- }
return f, nil
}
- return nil, pathError("open", name, syscall.ENOENT)
+ f = fakeDir{
+ fakeFile: fakeFile{
+ fi: item.fi,
+ },
+ entries: slices.Clone(item.children),
+ }
+ return f, nil
}
// Lstat returns the FileInfo structure describing the named file.
-// If the file is a symbolic link, the returned FileInfo
-// describes the symbolic link. Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *os.PathError.
func (fs *Reader) Lstat(name string) (*ExtendedFileInfo, error) {
- getDirInfo := func(name string) *ExtendedFileInfo {
- return &ExtendedFileInfo{
- Name: fs.Base(name),
- Size: 0,
- Mode: os.ModeDir | 0755,
- ModTime: time.Now(),
- }
+ name = readerCleanPath(name)
+ item, ok := fs.items[name]
+ if !ok {
+ return nil, pathError("lstat", name, os.ErrNotExist)
}
-
- switch name {
- case fs.Name:
- return fs.fi(), nil
- case "/", ".":
- return getDirInfo(name), nil
- }
-
- dir := fs.Dir(fs.Name)
- for {
- if dir == "/" || dir == "." {
- break
- }
- if name == dir {
- return getDirInfo(name), nil
- }
- dir = fs.Dir(dir)
- }
-
- return nil, pathError("lstat", name, os.ErrNotExist)
+ return item.fi, nil
}
// Join joins any number of path elements into a single path, adding a
@@ -137,7 +175,7 @@ func (fs *Reader) IsAbs(_ string) bool {
//
// For the Reader, all paths are absolute.
func (fs *Reader) Abs(p string) (string, error) {
- return path.Clean(p), nil
+ return readerCleanPath(p), nil
}
// Clean returns the cleaned path. For details, see filepath.Clean.
diff --git a/internal/fs/fs_reader_test.go b/internal/fs/fs_reader_test.go
index 257bfbbac..083e8a1a5 100644
--- a/internal/fs/fs_reader_test.go
+++ b/internal/fs/fs_reader_test.go
@@ -17,19 +17,11 @@ import (
func verifyFileContentOpenFile(t testing.TB, fs FS, filename string, want []byte) {
f, err := fs.OpenFile(filename, O_RDONLY, false)
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
buf, err := io.ReadAll(f)
- if err != nil {
- t.Fatal(err)
- }
-
- err = f.Close()
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
+ test.OK(t, f.Close())
if !cmp.Equal(want, buf) {
t.Error(cmp.Diff(want, buf))
@@ -38,19 +30,11 @@ func verifyFileContentOpenFile(t testing.TB, fs FS, filename string, want []byte
func verifyDirectoryContents(t testing.TB, fs FS, dir string, want []string) {
f, err := fs.OpenFile(dir, O_RDONLY, false)
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
entries, err := f.Readdirnames(-1)
- if err != nil {
- t.Fatal(err)
- }
-
- err = f.Close()
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
+ test.OK(t, f.Close())
sort.Strings(want)
sort.Strings(entries)
@@ -69,7 +53,7 @@ func checkFileInfo(t testing.TB, fi *ExtendedFileInfo, filename string, modtime
t.Errorf("Mode has wrong value, want 0%o, got 0%o", mode, fi.Mode)
}
- if !modtime.Equal(time.Time{}) && !fi.ModTime.Equal(modtime) {
+ if !fi.ModTime.Equal(modtime) {
t.Errorf("ModTime has wrong value, want %v, got %v", modtime, fi.ModTime)
}
@@ -82,40 +66,48 @@ func checkFileInfo(t testing.TB, fi *ExtendedFileInfo, filename string, modtime
}
}
-func TestFSReader(t *testing.T) {
- data := test.Random(55, 1<<18+588)
- now := time.Now()
- filename := "foobar"
+type fsTest []struct {
+ name string
+ f func(t *testing.T, fs FS)
+}
- var tests = []struct {
- name string
- f func(t *testing.T, fs FS)
- }{
+func createReadDirTest(fpath, filename string) fsTest {
+ return fsTest{
{
- name: "Readdirnames-slash",
+ name: "Readdirnames-slash-" + fpath,
f: func(t *testing.T, fs FS) {
- verifyDirectoryContents(t, fs, "/", []string{filename})
+ verifyDirectoryContents(t, fs, "/"+fpath, []string{filename})
},
},
{
- name: "Readdirnames-current",
+ name: "Readdirnames-current-" + fpath,
f: func(t *testing.T, fs FS) {
- verifyDirectoryContents(t, fs, ".", []string{filename})
+ verifyDirectoryContents(t, fs, path.Clean(fpath), []string{filename})
},
},
+ }
+}
+
+func createFileTest(filename string, now time.Time, data []byte) fsTest {
+ return fsTest{
{
name: "file/OpenFile",
f: func(t *testing.T, fs FS) {
verifyFileContentOpenFile(t, fs, filename, data)
},
},
+ {
+ name: "file/Open-error-not-exist",
+ f: func(t *testing.T, fs FS) {
+ _, err := fs.OpenFile(filename+"/other", O_RDONLY, false)
+ test.Assert(t, errors.Is(err, os.ErrNotExist), "unexpected error, got %v, expected %v", err, os.ErrNotExist)
+ },
+ },
{
name: "file/Lstat",
f: func(t *testing.T, fs FS) {
fi, err := fs.Lstat(filename)
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
checkFileInfo(t, fi, filename, now, 0644, false)
},
@@ -123,91 +115,113 @@ func TestFSReader(t *testing.T) {
{
name: "file/Stat",
f: func(t *testing.T, fs FS) {
- f, err := fs.OpenFile(filename, O_RDONLY, true)
- if err != nil {
- t.Fatal(err)
- }
-
- fi, err := f.Stat()
- if err != nil {
- t.Fatal(err)
- }
-
- err = f.Close()
- if err != nil {
- t.Fatal(err)
- }
-
+ fi := fsOpenAndStat(t, fs, filename, true)
checkFileInfo(t, fi, filename, now, 0644, false)
},
},
- {
- name: "dir/Lstat-slash",
- f: func(t *testing.T, fs FS) {
- fi, err := fs.Lstat("/")
- if err != nil {
- t.Fatal(err)
- }
+ }
+}
- checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true)
+func createDirTest(fpath string, now time.Time) fsTest {
+ return fsTest{
+ {
+ name: "dir/Lstat-slash-" + fpath,
+ f: func(t *testing.T, fs FS) {
+ fi, err := fs.Lstat("/" + fpath)
+ test.OK(t, err)
+
+ checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
},
},
{
- name: "dir/Lstat-current",
+ name: "dir/Lstat-current-" + fpath,
f: func(t *testing.T, fs FS) {
- fi, err := fs.Lstat(".")
- if err != nil {
- t.Fatal(err)
- }
+ fi, err := fs.Lstat("./" + fpath)
+ test.OK(t, err)
- checkFileInfo(t, fi, ".", time.Time{}, os.ModeDir|0755, true)
+ checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
},
},
{
- name: "dir/Lstat-error-not-exist",
+ name: "dir/Lstat-error-not-exist-" + fpath,
f: func(t *testing.T, fs FS) {
- _, err := fs.Lstat("other")
- if !errors.Is(err, os.ErrNotExist) {
- t.Fatal(err)
- }
+ _, err := fs.Lstat(fpath + "/other")
+ test.Assert(t, errors.Is(err, os.ErrNotExist), "unexpected error, got %v, expected %v", err, os.ErrNotExist)
},
},
{
- name: "dir/Open-slash",
+ name: "dir/Open-slash-" + fpath,
f: func(t *testing.T, fs FS) {
- fi, err := fs.Lstat("/")
- if err != nil {
- t.Fatal(err)
- }
-
- checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true)
+ fi := fsOpenAndStat(t, fs, "/"+fpath, false)
+ checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
},
},
{
- name: "dir/Open-current",
+ name: "dir/Open-current-" + fpath,
f: func(t *testing.T, fs FS) {
- fi, err := fs.Lstat(".")
- if err != nil {
- t.Fatal(err)
- }
-
- checkFileInfo(t, fi, ".", time.Time{}, os.ModeDir|0755, true)
+ fi := fsOpenAndStat(t, fs, "./"+fpath, false)
+ checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
},
},
}
+}
- for _, test := range tests {
- fs := &Reader{
- Name: filename,
- ReadCloser: io.NopCloser(bytes.NewReader(data)),
+func fsOpenAndStat(t *testing.T, fs FS, fpath string, metadataOnly bool) *ExtendedFileInfo {
+ f, err := fs.OpenFile(fpath, O_RDONLY, metadataOnly)
+ test.OK(t, err)
+ fi, err := f.Stat()
+ test.OK(t, err)
+ test.OK(t, f.Close())
+ return fi
+}
+
+func TestFSReader(t *testing.T) {
+ data := test.Random(55, 1<<18+588)
+ now := time.Now()
+ filename := "foobar"
+
+ tests := createReadDirTest("", filename)
+ tests = append(tests, createFileTest(filename, now, data)...)
+ tests = append(tests, createDirTest("", now)...)
+
+ for _, tst := range tests {
+ fs, err := NewReader(filename, io.NopCloser(bytes.NewReader(data)), ReaderOptions{
Mode: 0644,
Size: int64(len(data)),
ModTime: now,
- }
+ })
+ test.OK(t, err)
- t.Run(test.name, func(t *testing.T) {
- test.f(t, fs)
+ t.Run(tst.name, func(t *testing.T) {
+ tst.f(t, fs)
+ })
+ }
+}
+
+func TestFSReaderNested(t *testing.T) {
+ data := test.Random(55, 1<<18+588)
+ now := time.Now()
+ filename := "foo/sub/bar"
+
+ tests := createReadDirTest("", "foo")
+ tests = append(tests, createReadDirTest("foo", "sub")...)
+ tests = append(tests, createReadDirTest("foo/sub", "bar")...)
+ tests = append(tests, createFileTest(filename, now, data)...)
+ tests = append(tests, createDirTest("", now)...)
+ tests = append(tests, createDirTest("foo", now)...)
+ tests = append(tests, createDirTest("foo/sub", now)...)
+
+ for _, tst := range tests {
+ fs, err := NewReader(filename, io.NopCloser(bytes.NewReader(data)), ReaderOptions{
+ Mode: 0644,
+ Size: int64(len(data)),
+ ModTime: now,
+ })
+ test.OK(t, err)
+
+ t.Run(tst.name, func(t *testing.T) {
+ tst.f(t, fs)
})
}
}
@@ -230,29 +244,24 @@ func TestFSReaderDir(t *testing.T) {
},
}
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- fs := &Reader{
- Name: test.filename,
- ReadCloser: io.NopCloser(bytes.NewReader(data)),
-
+ for _, tst := range tests {
+ t.Run(tst.name, func(t *testing.T) {
+ fs, err := NewReader(tst.filename, io.NopCloser(bytes.NewReader(data)), ReaderOptions{
Mode: 0644,
Size: int64(len(data)),
ModTime: now,
- }
-
- dir := path.Dir(fs.Name)
+ })
+ test.OK(t, err)
+ dir := path.Dir(tst.filename)
for {
if dir == "/" || dir == "." {
break
}
fi, err := fs.Lstat(dir)
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
- checkFileInfo(t, fi, dir, time.Time{}, os.ModeDir|0755, true)
+ checkFileInfo(t, fi, dir, now, os.ModeDir|0755, true)
dir = path.Dir(dir)
}
@@ -285,40 +294,30 @@ func TestFSReaderMinFileSize(t *testing.T) {
},
}
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- fs := &Reader{
- Name: "testfile",
- ReadCloser: io.NopCloser(strings.NewReader(test.data)),
+ for _, tst := range tests {
+ t.Run(tst.name, func(t *testing.T) {
+ fs, err := NewReader("testfile", io.NopCloser(strings.NewReader(tst.data)), ReaderOptions{
Mode: 0644,
ModTime: time.Now(),
- AllowEmptyFile: test.allowEmpty,
- }
-
+ AllowEmptyFile: tst.allowEmpty,
+ })
+ test.OK(t, err)
f, err := fs.OpenFile("testfile", O_RDONLY, false)
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
buf, err := io.ReadAll(f)
- if test.readMustErr {
+ if tst.readMustErr {
if err == nil {
t.Fatal("expected error not found, got nil")
}
} else {
- if err != nil {
- t.Fatal(err)
- }
+ test.OK(t, err)
}
- if string(buf) != test.data {
- t.Fatalf("wrong data returned, want %q, got %q", test.data, string(buf))
- }
-
- err = f.Close()
- if err != nil {
- t.Fatal(err)
+ if string(buf) != tst.data {
+ t.Fatalf("wrong data returned, want %q, got %q", tst.data, string(buf))
}
+ test.OK(t, f.Close())
})
}
}
diff --git a/internal/fs/node_xattr.go b/internal/fs/node_xattr.go
index 2a2b5c0fb..f1119fe51 100644
--- a/internal/fs/node_xattr.go
+++ b/internal/fs/node_xattr.go
@@ -53,9 +53,8 @@ func handleXattrErr(err error) error {
case *xattr.Error:
// On Linux, xattr calls on files in an SMB/CIFS mount can return
- // ENOATTR instead of ENOTSUP.
- switch e.Err {
- case syscall.ENOTSUP, xattr.ENOATTR:
+ // ENOATTR instead of ENOTSUP. BSD can return EOPNOTSUPP.
+ if e.Err == syscall.ENOTSUP || e.Err == syscall.EOPNOTSUPP || e.Err == xattr.ENOATTR {
return nil
}
return errors.WithStack(e)
diff --git a/internal/restic/duration.go b/internal/restic/duration.go
index 831971fe0..6ab4c3c79 100644
--- a/internal/restic/duration.go
+++ b/internal/restic/duration.go
@@ -4,7 +4,6 @@ import (
"fmt"
"strconv"
"strings"
- "unicode"
"github.com/restic/restic/internal/errors"
)
@@ -52,7 +51,7 @@ func nextNumber(input string) (num int, rest string, err error) {
}
for i, s := range input {
- if !unicode.IsNumber(s) {
+ if s < '0' || s > '9' {
rest = input[i:]
break
}
diff --git a/internal/restic/duration_test.go b/internal/restic/duration_test.go
index f03aa5553..c5f0390db 100644
--- a/internal/restic/duration_test.go
+++ b/internal/restic/duration_test.go
@@ -37,6 +37,9 @@ func TestNextNumber(t *testing.T) {
{
input: "5d ", num: 5, rest: "d ",
},
+ {
+ input: "5", num: 5, rest: "",
+ },
}
for _, test := range tests {
@@ -78,6 +81,7 @@ func TestParseDuration(t *testing.T) {
{input: "2w", err: true},
{input: "1y4m3w1d", err: true},
{input: "s", err: true},
+ {input: "\xdf\x80", err: true}, // NKO DIGIT ZERO; we want ASCII digits
}
for _, test := range tests {
diff --git a/internal/restorer/restorer_test.go b/internal/restorer/restorer_test.go
index b48ae137c..0b5e34d12 100644
--- a/internal/restorer/restorer_test.go
+++ b/internal/restorer/restorer_test.go
@@ -908,13 +908,12 @@ func TestRestorerSparseFiles(t *testing.T) {
var zeros [1<<20 + 13]byte
- target := &fs.Reader{
- Mode: 0600,
- Name: "/zeros",
- ReadCloser: io.NopCloser(bytes.NewReader(zeros[:])),
- }
+ target, err := fs.NewReader("/zeros", io.NopCloser(bytes.NewReader(zeros[:])), fs.ReaderOptions{
+ Mode: 0600,
+ })
+ rtest.OK(t, err)
sc := archiver.NewScanner(target)
- err := sc.Scan(context.TODO(), []string{"/zeros"})
+ err = sc.Scan(context.TODO(), []string{"/zeros"})
rtest.OK(t, err)
arch := archiver.New(repo, target, archiver.Options{})
diff --git a/internal/walker/walker.go b/internal/walker/walker.go
index 252bc3530..0d69106bf 100644
--- a/internal/walker/walker.go
+++ b/internal/walker/walker.go
@@ -91,6 +91,8 @@ func walk(ctx context.Context, repo restic.BlobLoader, prefix string, parentTree
if err == ErrSkipNode {
continue
}
+
+ return err
}
err = walk(ctx, repo, p, *node.Subtree, subtree, visitor)
diff --git a/internal/walker/walker_test.go b/internal/walker/walker_test.go
index 3614a2397..47d872d97 100644
--- a/internal/walker/walker_test.go
+++ b/internal/walker/walker_test.go
@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/restic/restic/internal/restic"
+ rtest "github.com/restic/restic/internal/test"
)
// TestTree is used to construct a list of trees for testing the walker.
@@ -93,12 +94,12 @@ func (t TreeMap) Connections() uint {
// checkFunc returns a function suitable for walking the tree to check
// something, and a function which will check the final result.
-type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB))
+type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error))
// checkItemOrder ensures that the order of the 'path' arguments is the one passed in as 'want'.
func checkItemOrder(want []string) checkFunc {
pos := 0
- return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
+ return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
if err != nil {
t.Errorf("error walking %v: %v", path, err)
@@ -121,7 +122,8 @@ func checkItemOrder(want []string) checkFunc {
return walker(restic.ID{}, "leave: "+path, nil, nil)
}
- final = func(t testing.TB) {
+ final = func(t testing.TB, err error) {
+ rtest.OK(t, err)
if pos != len(want) {
t.Errorf("not enough items returned, want %d, got %d", len(want), pos)
}
@@ -134,7 +136,7 @@ func checkItemOrder(want []string) checkFunc {
// checkParentTreeOrder ensures that the order of the 'parentID' arguments is the one passed in as 'want'.
func checkParentTreeOrder(want []string) checkFunc {
pos := 0
- return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
+ return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
if err != nil {
t.Errorf("error walking %v: %v", path, err)
@@ -153,7 +155,8 @@ func checkParentTreeOrder(want []string) checkFunc {
return nil
}
- final = func(t testing.TB) {
+ final = func(t testing.TB, err error) {
+ rtest.OK(t, err)
if pos != len(want) {
t.Errorf("not enough items returned, want %d, got %d", len(want), pos)
}
@@ -168,7 +171,7 @@ func checkParentTreeOrder(want []string) checkFunc {
func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
var pos int
- return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
+ return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
if err != nil {
t.Errorf("error walking %v: %v", path, err)
@@ -196,7 +199,8 @@ func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
return walker(restic.ID{}, "leave: "+path, nil, nil)
}
- final = func(t testing.TB) {
+ final = func(t testing.TB, err error) {
+ rtest.OK(t, err)
if pos != len(wantPaths) {
t.Errorf("wrong number of paths returned, want %d, got %d", len(wantPaths), pos)
}
@@ -206,6 +210,32 @@ func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
}
}
+func checkErrorReturned(errForPath string) checkFunc {
+ expectedErr := fmt.Errorf("error for %v", errForPath)
+
+ return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
+ walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
+ if path == errForPath {
+ return expectedErr
+ }
+ return nil
+ }
+
+ leaveDir = func(path string) error {
+ return walker(restic.ID{}, "leave: "+path, nil, nil)
+ }
+
+ final = func(t testing.TB, err error) {
+ if err == nil {
+ t.Errorf("expected error for %v, got nil", errForPath)
+ }
+ rtest.Assert(t, err == expectedErr, "expected error for %v, got %v", errForPath, err)
+ }
+
+ return walker, leaveDir, final
+ }
+}
+
func TestWalker(t *testing.T) {
var tests = []struct {
tree TestTree
@@ -427,6 +457,21 @@ func TestWalker(t *testing.T) {
}),
},
},
+ {
+ tree: TestTree{
+ "subdir1": TestTree{
+ "file": TestFile{},
+ },
+ "subdir2": TestTree{},
+ },
+ checks: []checkFunc{
+ checkErrorReturned("/subdir1"),
+ checkErrorReturned("/subdir2"),
+ checkErrorReturned("/subdir1/file"),
+ checkErrorReturned("leave: /subdir1"),
+ checkErrorReturned("leave: /subdir2"),
+ },
+ },
}
for _, test := range tests {
@@ -442,10 +487,7 @@ func TestWalker(t *testing.T) {
ProcessNode: fn,
LeaveDir: leaveDir,
})
- if err != nil {
- t.Error(err)
- }
- last(t)
+ last(t, err)
})
}
})