Compare commits

...

108 Commits

Author SHA1 Message Date
Leo R. Lundgren
6d39410958 doc: Reword parts of the text, replace rclone with rest-server
The intent here is to make the text more consistent in its use of different
concepts involved in explaining the idea and setup that is explained, and
to make it easier to follow.

We're also replacing rclone with rest-server, not because we dislike rclone
but in order to keep the text to the basic tooling and the main restic eco-
system.

Finally we also remove the previous tip at the end about keeping the SSH tunnel
up, as it will be during the time the SSH session is running (in which the user
is expected to run the restic commands).
2026-02-18 22:19:56 +01:00
JL710
5c3116901e use rclone for rest server instead of docker 2026-01-17 16:29:26 +01:00
JL710
8943ca15ed apply suggestions from Michael Eischer 2025-09-25 13:35:40 +02:00
JL710
a9d51db68d add example for "Pulling a Backup with HTTP over a ssh tunnel" 2025-09-11 16:07:50 +02:00
Michael Eischer
cc1fe6c111 Merge pull request #5493 from greatroar/ioctl
backend,termstatus: Unify foreground/background detection
2025-09-08 10:40:36 +02:00
greatroar
1ed93bd54d backend,termstatus: Unify foreground/background detection
PR #5358 reintroduced a version of the TIOCGPGRP ioctl call that works
on all Unix platforms except Linux, due to a bug/inconsistency in
x/sys/unix. This commit fixes that by introducing termstatus.Tcgetpgrp.

It also introduces termstatus.Getpgrp and termstatus.Tcsetpgrp to deal
with the different signature of unix.Getpgrp in Solaris vs. all other
Unix platforms and an int-overflowing constant on AIX, so that some
AIX/Solaris-specific code can be removed elsewhere and
foreground/background detection is done the same everywhere except on
Windows.
2025-09-08 09:33:44 +02:00
Michael Eischer
c55df65bb6 Merge pull request #5490 from MichaelEischer/sync-compatibility-section
docs: sync compatibility section with website
2025-09-07 11:35:48 +02:00
Michael Eischer
289adebbb7 Merge pull request #5492 from MichaelEischer/update-dependencies
update dependencies
2025-09-07 11:34:37 +02:00
Michael Eischer
eef047cfa4 update dependencies 2025-09-06 22:58:30 +02:00
Michael Eischer
7f1ac5764a Merge pull request #5358 from MichaelEischer/fix-tty-capture-in-background
Fix tty capture when started in background
2025-09-06 21:01:03 +02:00
Michael Eischer
52aa1cd17f docs: sync compatibility section with website
This is no change in policy, just a more precise description of the
status quo.
2025-09-06 20:55:48 +02:00
Michael Eischer
42f690dbab add changelog 2025-09-06 20:50:06 +02:00
Michael Eischer
914bd699be backend: always start backend process in separate process group
The process group is necessary to properly handle ctrl-c.
2025-09-06 19:51:10 +02:00
Michael Eischer
4c19d6410f backend: only move child process to foreground if already in foreground
The rclone and sftp backends require starting a child process. The are
first moved into the foreground and back into the background after the
initial startup is done.

However, this behavior is also active if restic itself is started in the
background. In this case, restic changing the foreground process may
confuse the shell and in case of bash causes it to exit. Thus, disable
modification of the controlling process group of the tty if restic is
run in the background.
2025-09-06 19:51:10 +02:00
Michael Eischer
2ad703bfd8 Merge pull request #5486 from greatroar/duration-panic
Avoid panic in duration parsing
2025-09-06 18:32:36 +02:00
greatroar
0864d04c5c internal/restic: Fix panic in ParseDuration
Fixes #5485. Includes test case by @MichaelEischer.
2025-09-06 10:30:34 +02:00
gregoster
839c38b4c4 EOPNOTSUPP can be returned if the filesystem does not support xattrs (#5344)
---------

Co-authored-by: Greg Oster <oster@netbsd.org>
2025-09-05 19:09:27 +00:00
Michael Eischer
e98c44baaf Merge pull request #5421 from MichaelEischer/fix-backup-metadata-crash
backup: fix possible crash if directory disappears before metadata collection
2025-09-05 19:53:16 +02:00
Michael Eischer
01bc60e96f backup: fix test on windows 2025-09-05 19:35:25 +02:00
Michael Eischer
484b706dd8 add changelog 2025-09-05 19:28:18 +02:00
Michael Eischer
350f6452e7 backup: test that parent directory errors can be correctly filtered 2025-09-05 19:28:18 +02:00
Michael Eischer
484cdf12e5 backup: test that missing parent directory is correctly handled 2025-09-05 19:28:18 +02:00
Michael Eischer
c8bb7bd312 backup: do not fail backup is some parent folder is inaccessible
Handle errors for parent directories of backup directories in the same
way as all other file access errors during a backup.
2025-09-05 19:28:18 +02:00
Michael Eischer
a8ce2e45cc backup: do not crash if nodeFromFileInfo fails
this could crash in two cases:
- if a directory is deleted between restic stating it and trying to list
  its directory content.
- when restic tries to list the parent directory of a backup target, but
  the parent directory has been deleted.

return an error in this case instead.
2025-09-05 19:28:18 +02:00
Michael Eischer
275507fb3e Merge pull request #5452 from dominikschulz/fix/storage
Mark HTTP Error 507 as permanent
2025-09-05 19:17:15 +02:00
Michael Eischer
391c27975a Merge pull request #5460 from restic/dependabot/go_modules/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob-1.6.2
build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/storage/azblob from 1.6.1 to 1.6.2
2025-09-05 19:16:54 +02:00
Michael Eischer
7d47e60e27 Merge pull request #5481 from restic/dependabot/github_actions/actions/checkout-5
build(deps): bump actions/checkout from 4 to 5
2025-09-05 19:07:58 +02:00
Michael Eischer
5c17c277f3 add changelog 2025-09-05 19:07:09 +02:00
Dominik Schulz
48e5c0984e Mark HTTP Error 507 as permanent
This change classifies HTTP error 507 (Insufficient Storage) as a
permanent error that should not be retried. I keep running into
this once in a while and there is literally no point in retrying when
the server is full.

Fixes #5429

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
2025-09-05 19:04:16 +02:00
dependabot[bot]
2414771a59 build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/storage/azblob
Bumps [github.com/Azure/azure-sdk-for-go/sdk/storage/azblob](https://github.com/Azure/azure-sdk-for-go) from 1.6.1 to 1.6.2.
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/go-mgmt-sdk-release-guideline.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.6.1...sdk/storage/azblob/v1.6.2)

---
updated-dependencies:
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/storage/azblob
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 16:58:21 +00:00
Michael Eischer
0e381bdbf1 Merge pull request #5483 from restic/dependabot/go_modules/google.golang.org/api-0.248.0
build(deps): bump google.golang.org/api from 0.228.0 to 0.248.0
2025-09-05 18:47:58 +02:00
dependabot[bot]
11cd4a0a88 build(deps): bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 16:38:46 +00:00
dependabot[bot]
f54989f634 build(deps): bump google.golang.org/api from 0.228.0 to 0.248.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.228.0 to 0.248.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.228.0...v0.248.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-version: 0.248.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 16:36:17 +00:00
Michael Eischer
af7f10d16b Merge pull request #5482 from restic/dependabot/github_actions/docker/login-action-3.5.0
build(deps): bump docker/login-action from 3.4.0 to 3.5.0
2025-09-05 18:35:59 +02:00
Michael Eischer
6de95a3d58 Merge pull request #5484 from restic/dependabot/go_modules/golang.org/x/crypto-0.41.0
build(deps): bump golang.org/x/crypto from 0.39.0 to 0.41.0
2025-09-05 18:35:24 +02:00
dependabot[bot]
93f436e999 build(deps): bump golang.org/x/crypto from 0.39.0 to 0.41.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.39.0 to 0.41.0.
- [Commits](https://github.com/golang/crypto/compare/v0.39.0...v0.41.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.41.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 02:10:03 +00:00
dependabot[bot]
6edb199efd build(deps): bump docker/login-action from 3.4.0 to 3.5.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](74a5d14239...184bdaa072)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 01:57:28 +00:00
rawtaz
9b2c0a0c54 Merge pull request #5466 from y0n3d4/gentoo-install-info
Added Gentoo install info
2025-08-07 13:28:17 +02:00
y0n3d4
64273ea027 Update 020_installation.rst removing command options
Removed command options: their use is a user choice
2025-08-06 13:57:29 +02:00
Michele Testa
5a00d26431 Update 020_installation.rst adding instruction for Gentoo Linux 2025-08-05 15:16:37 +02:00
Michele Testa
3faad5751d Revert "Update 020_installation.rst adding install command for Gentoo Linux"
This reverts commit f487eb1c66.
2025-08-05 14:12:04 +02:00
y0n3d4
f487eb1c66 Update 020_installation.rst adding install command for Gentoo Linux
Added basic instructions for restic installation on Gentoo using the official package manager (Portage)
2025-08-05 14:01:10 +02:00
Michael Eischer
72636238d0 Merge pull request #5400 from rhhub/patch-2
docs: clarify ** wildcard must me between path separators
2025-08-04 20:56:07 +02:00
rawtaz
51098157e2 Merge pull request #5462 from TheAlchemistOf42/wasabi-link
Correct Wasabi link
2025-08-02 22:12:57 +02:00
A Crutcher
0b080c44d7 doc: Correct Wasabi link 2025-08-02 13:03:43 -05:00
Michael Eischer
b71fe91643 Merge pull request #5345 from mikix/chmod-enotsup
backend/local: ignore chmod "not supported" errors
2025-07-21 22:28:53 +02:00
Michael Eischer
9c3b8d171a Merge pull request #5434 from restic/dependabot/go_modules/golang.org/x/time-0.12.0
build(deps): bump golang.org/x/time from 0.11.0 to 0.12.0
2025-07-21 22:17:40 +02:00
Michael Eischer
ddb7fb837b Merge pull request #5435 from restic/dependabot/go_modules/github.com/Azure/azure-sdk-for-go/sdk/azidentity-1.10.1
build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity from 1.10.0 to 1.10.1
2025-07-21 22:14:29 +02:00
Michael Eischer
3433c5abac Merge pull request #5408 from MichaelEischer/fix-walker-crash
walker: fix error handling if tree cannot be loaded
2025-07-21 21:46:59 +02:00
dependabot[bot]
09bc58c950 build(deps): bump golang.org/x/time from 0.11.0 to 0.12.0
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.11.0 to 0.12.0.
- [Commits](https://github.com/golang/time/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/time
  dependency-version: 0.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 19:46:35 +00:00
Michael Eischer
20eb9018a0 Merge pull request #5409 from MichaelEischer/fix-release-notes-typos
Fix release note typos
2025-07-21 21:46:05 +02:00
Michael Eischer
651f553530 Merge pull request #5436 from restic/dependabot/go_modules/github.com/peterbourgon/unixtransport-0.0.6
build(deps): bump github.com/peterbourgon/unixtransport from 0.0.4 to 0.0.6
2025-07-21 21:45:06 +02:00
Michael Eischer
aad4b53ead Merge pull request #5438 from restic/dependabot/go_modules/golang.org/x/crypto-0.39.0
build(deps): bump golang.org/x/crypto from 0.38.0 to 0.39.0
2025-07-21 21:42:06 +02:00
dependabot[bot]
e467496ace build(deps): bump golang.org/x/crypto from 0.38.0 to 0.39.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.38.0 to 0.39.0.
- [Commits](https://github.com/golang/crypto/compare/v0.38.0...v0.39.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 05:46:21 +00:00
dependabot[bot]
f2de260524 build(deps): bump github.com/peterbourgon/unixtransport
Bumps [github.com/peterbourgon/unixtransport](https://github.com/peterbourgon/unixtransport) from 0.0.4 to 0.0.6.
- [Release notes](https://github.com/peterbourgon/unixtransport/releases)
- [Commits](https://github.com/peterbourgon/unixtransport/compare/v0.0.4...v0.0.6)

---
updated-dependencies:
- dependency-name: github.com/peterbourgon/unixtransport
  dependency-version: 0.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 05:07:50 +00:00
dependabot[bot]
c17d5ab2e1 build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity
Bumps [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/go-mgmt-sdk-release-guideline.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.10.0...sdk/azidentity/v1.10.1)

---
updated-dependencies:
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity
  dependency-version: 1.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 05:07:41 +00:00
Michael Terry
a8535aba58 backend/local: ignore chmod "not supported" errors 2025-06-02 17:44:30 -04:00
rhhub
521fbad701 docs: clarify ** must me between path separators 2025-06-02 13:46:06 -07:00
Michael Eischer
15b7d7c3fc Fix release note typos 2025-06-02 21:12:37 +02:00
Patrick Wolf
7d39b1bfe8 Update 047_tuning_backup_parameters.rst - local backend (#5355)
users would find it helpful to know how to adjust the "local" backend and they might not get the idea that the local backend is just called local... which in turn leads them to think restic is slow as they can't adjust away  from 2 threads for restore and backup.
2025-06-02 18:40:04 +00:00
Michael Eischer
e4a7f4aadf Merge pull request #5356 from MichaelEischer/fix-backup-stdin-filename
backup: Fix `--stdin-filename` with directory
2025-06-02 20:27:26 +02:00
Michael Eischer
10cfe96cd4 walker: fix error handling if tree cannot be loaded
A tree that cannot be loaded is a fatal error when walking the tree.
Thus, return the error and exit the tree walk.
2025-06-02 20:04:26 +02:00
Michael Eischer
2eaa79d33f Merge pull request #5374 from ilyagr/docprofile
docs: document profiling options a bit better
2025-06-02 19:57:33 +02:00
Ilya Grigoriev
99ee5696f3 bugfix: have --{cpu,mem,...}-profile work even if Restic exits with error code (#5373)
* bugfix: write pprof file for `--{cpu,mem,...}-profile` even on error code

Before this, if `restic backup --cpu-profile dir/ backup-dir/` couldn't
read some of the input files (e.g. they weren't readable by the user
restic was running under), the `cpu.pprof` file it outputs would be
empty.

https://github.com/spf13/cobra/issues/1893

* drop changelog as it's not relevant for end users

---------

Co-authored-by: Michael Eischer <michael.eischer@fau.de>
2025-06-02 17:57:07 +00:00
Ilya Grigoriev
e8dbb69a94 docs: when describing profiling, briefly explain .pprof files 2025-06-02 19:49:19 +02:00
Ilya Grigoriev
f4e21cdb75 docs: document profiling options a bit better
Previously, the docs were a bit mysterious about what "enables profiling
support" means or how one could take advantage of it.
2025-06-02 19:49:19 +02:00
Michael Eischer
e5bdc3c74f Merge pull request #5382 from restic/dependabot/go_modules/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob-1.6.1
build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/storage/azblob from 1.6.0 to 1.6.1
2025-06-02 19:40:33 +02:00
Michael Eischer
7e51c928c4 Merge pull request #5384 from zmanda/feat-gh-5377-check-add-percentage-for-read-data-subset
check: add percentage of repository checked
2025-06-02 19:37:08 +02:00
dependabot[bot]
21e87851aa build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/storage/azblob
Bumps [github.com/Azure/azure-sdk-for-go/sdk/storage/azblob](https://github.com/Azure/azure-sdk-for-go) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.6.0...sdk/azcore/v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/storage/azblob
  dependency-version: 1.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 17:32:18 +00:00
Michael Eischer
2bc1bf2702 Merge pull request #5402 from restic/dependabot/go_modules/github.com/Azure/azure-sdk-for-go/sdk/azidentity-1.10.0
build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity from 1.8.2 to 1.10.0
2025-06-02 19:19:10 +02:00
dependabot[bot]
df110060d1 build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity
Bumps [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) from 1.8.2 to 1.10.0.
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/go-mgmt-sdk-release-guideline.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azidentity/v1.8.2...sdk/azcore/v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity
  dependency-version: 1.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 16:45:38 +00:00
Michael Eischer
337a7d1205 Merge pull request #5385 from Silvenga/windows-docs
docs: updated installation docs for Windows
2025-06-02 18:44:43 +02:00
Michael Eischer
322e271dd2 Merge pull request #5404 from restic/dependabot/go_modules/golang.org/x/sys-0.33.0
build(deps): bump golang.org/x/sys from 0.31.0 to 0.33.0
2025-06-02 18:35:34 +02:00
dependabot[bot]
1ac224458f build(deps): bump golang.org/x/sys from 0.31.0 to 0.33.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.31.0 to 0.33.0.
- [Commits](https://github.com/golang/sys/compare/v0.31.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-01 01:25:20 +00:00
rawtaz
126ad04568 Merge pull request #5389 from fronesis47/patch-3
Update passwords section of FAQ
2025-05-11 20:35:20 +02:00
Samuel Chambers
e732bdbfb8 updated doc/faq.rst_commitsSquashed 2025-05-11 14:02:07 -04:00
Mark Lopez
2db08fd749 docs: updated installation docs for Windows 2025-05-03 10:27:50 -05:00
Srigovind Nayak
debb110a7c check: add percentage of repository checked 2025-05-03 18:08:12 +05:30
rawtaz
5eb4f5af61 Merge pull request #5360 from makuhama/master
doc: typo & minor rewording in 'Removing files from snapshots'
2025-05-02 01:05:08 +02:00
Markus Hansmair
287b601f01 doc: typo & minor rewording in 'Removing files from snapshots' 2025-04-16 09:37:26 +02:00
Michael Eischer
64c82a5d9c Merge pull request #5357 from Hello71/patch-1
doc: add fastest, better compression
2025-04-14 20:02:41 +02:00
Alex Xu
12f36ebf07 doc: add fastest, better compression
Follow-up for #5321
2025-04-13 19:33:13 -04:00
Michael Eischer
45e09dca2a add changelog for --stdin-filename with/directory 2025-04-11 22:29:18 +02:00
Michael Eischer
5bb9d0d996 backup: test subdirectories in stdin filenames work 2025-04-11 22:14:32 +02:00
Michael Eischer
9f39e8a1d3 fs/reader: return proper error on invalid filename 2025-04-11 22:07:31 +02:00
Michael Eischer
ddd48f1e98 fs/reader: test file not exist case 2025-04-11 21:57:45 +02:00
Michael Eischer
6e91ea3397 fs/reader: use test helpers 2025-04-11 21:54:15 +02:00
Michael Eischer
e7c1e4f1ff fs/reader: deduplicate test code 2025-04-11 21:50:47 +02:00
Michael Eischer
70e1037a49 fs/reader: fix open+stat handling 2025-04-11 21:49:25 +02:00
Michael Eischer
19f48084ea fs/reader: use modification time for file and directories
This ensures that a fixed input generates a fully deterministic output
file structure.
2025-04-11 21:46:24 +02:00
Michael Eischer
3a995172b7 fs: rewrite Reader to build fs tree up front
This adds proper support for filenames that include directories. For
example, `/foo/bar` would result in an error when trying to open `/foo`.

The directory tree is now build upfront. This ensures let's the
directory tree construction be handled only once. All accessors then
only have to look up the constructed directory entries.
2025-04-11 21:37:40 +02:00
Michael Eischer
0dffa1208d Merge pull request #5243 from zmanda/feat-gh-4868-show-repo-id-in-df-and-mount
mount: append the repository ID to the name of the FUSE mount
2025-04-02 21:16:11 +02:00
Michael Eischer
6fbcce1d1a docs: fix typos in developer information (#5329) 2025-04-02 21:12:43 +02:00
Michael Eischer
e8d458be7e update direct dependencies (#5340) 2025-04-02 21:10:40 +02:00
dependabot[bot]
4471c7847b build(deps): bump docker/login-action from 3.3.0 to 3.4.0 (#5333)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](9780b0c442...74a5d14239)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 20:54:17 +02:00
Srigovind Nayak
f13e9c10a4 Add support for additional compression levels fastest and better (#5321)
* repository: expose addtional compression levels

* adding better and fastest compression levels for zstd

* repository: add changelog entry for issue-4728

* chore: fix golint issues

* chore: sort compression modes in the help text

* updating review comments
2025-03-31 21:21:12 +02:00
Michael Eischer
f768683162 Merge pull request #5322 from zmanda/fix-gh-5233-forget-failure-exit-codes
forget: return exit code 3 on partial removal of snapshots
2025-03-31 20:08:33 +02:00
Michael Eischer
0b7bdfed7e Merge pull request #5320 from mjnaderi/patch-1
doc: fix typos
2025-03-31 20:07:51 +02:00
Michael Eischer
a4fe94ec82 Merge pull request #5317 from gilbsgilbs/fix-s3-restore-timeout-unit
docs: fix unit for S3 restore timeout
2025-03-31 20:01:22 +02:00
Michael Eischer
6684d1d2f5 Merge pull request #5327 from MichaelEischer/fix-forget-hostname-default
forget: fix ignored RESTIC_HOST environment variable
2025-03-31 19:21:13 +02:00
Michael Eischer
e1f7522174 forget: fix ignored RESTIC_HOST environment variable 2025-03-31 18:10:17 +02:00
Srigovind Nayak
d1649affb2 chore: update changelog for issue-5233 2025-03-30 14:12:06 +05:30
Srigovind Nayak
936c783c0f forget: exit code 3 for snapshot removal failures 2025-03-30 14:11:32 +05:30
Mohammad Javad Naderi
5614cf4758 doc: fix typos 2025-03-29 12:40:47 +03:30
Srigovind Nayak
6db0d84ab0 changelog: add changelog entry for issue-4868 2025-03-29 13:27:50 +05:30
Srigovind Nayak
88b599c4f3 mount: append repository ID to FS name of FUSE mount
* update review comments

mount: append repository ID to FS name of the FUSE mount
2025-03-29 13:22:10 +05:30
Gilbert Gilb's
eefff0d793 docs: fix unit for S3 restore timeout
"d" is not a valid unit.
2025-03-27 21:20:50 +01:00
Alexander Neumann
3d14e92905 Set development version for 0.18.0 2025-03-27 20:17:36 +01:00
62 changed files with 997 additions and 579 deletions

View File

@@ -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 }}

View File

@@ -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

View File

@@ -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

View File

@@ -1 +1 @@
0.18.0
0.18.0-dev

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,7 @@
Enhancement: Added support for zstd compression levels `fastest` and `better`
Restic now supports the zstd compression modes `fastest` and `better`. Set the
environment variable `RESTIC_COMPRESSION` to `fastest` or `better` to use these
compression levels. This can also be set with the `--compression` flag.
https://github.com/restic/restic/issues/4728

View File

@@ -0,0 +1,14 @@
Enhancement: Include repository id in filesystem name used by `mount`
The filesystem created by restic's `mount` command now includes the repository
id in the filesystem name. The repository id is printed by restic when opening
a repository or can be looked up using `restic cat config`.
```
[restic-user@hostname restic]$ df ./test-mount/
Filesystem 1K-blocks Used Available Use% Mounted on
restic:d3b07384d1 0 0 0 - /mnt/my-restic-repo
```
https://github.com/restic/restic/issues/4868
https://github.com/restic/restic/pull/5243

View File

@@ -0,0 +1,8 @@
Bugfix: forget command returns exit code 3 on partial removal of snapshots
The `forget` command now returns exit code 3 when it fails to remove one or
more snapshots. Previously, it returned exit code 0, which could lead to
confusion if the command was used in a script.
https://github.com/restic/restic/issues/5233
https://github.com/restic/restic/pull/5322

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,12 @@
Bugfix: Allow use of rclone/sftp backend when running restic in background
When starting restic in the background, this could result in unexpected behavior
when using the rclone or sftp backend.
For example running `restic -r rclone:./example --insecure-no-password init &`
could cause the calling `bash` shell to exit unexpectedly.
This has been fixed.
https://github.com/restic/restic/issues/5354
https://github.com/restic/restic/pull/5358

View File

@@ -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

View File

@@ -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

View File

@@ -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}
}

View File

@@ -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)
}

View File

@@ -425,7 +425,8 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
subsetSize = repoSize
}
packs = selectRandomPacksByFileSize(chkr.GetPacks(), subsetSize, repoSize)
printer.P("read %d bytes of data packs\n", subsetSize)
percentage := float64(subsetSize) / float64(repoSize) * 100.0
printer.P("read %d bytes (%.1f%%) of data packs\n", subsetSize, percentage)
}
if packs == nil {
return summary, errors.Fatal("internal error: failed to select packs to check")

View File

@@ -41,6 +41,7 @@ EXIT STATUS
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 3 if there was an error removing one or more snapshots.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect.
@@ -62,6 +63,7 @@ Exit status is 12 if the password is incorrect.
type ForgetPolicyCount int
var ErrNegativePolicyCount = errors.New("negative values not allowed, use 'unlimited' instead")
var ErrFailedToRemoveOneOrMoreSnapshots = errors.New("failed to remove one or more snapshots")
func (c *ForgetPolicyCount) Set(s string) error {
switch s {
@@ -137,13 +139,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}
@@ -304,12 +307,15 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
return ctx.Err()
}
// these are the snapshots that failed to be removed
failedSnIDs := restic.NewIDSet()
if len(removeSnIDs) > 0 {
if !opts.DryRun {
bar := printer.NewCounter("files deleted")
err := restic.ParallelRemove(ctx, repo, removeSnIDs, restic.WriteableSnapshotFile, func(id restic.ID, err error) error {
if err != nil {
printer.E("unable to remove %v/%v from the repository\n", restic.SnapshotFile, id)
failedSnIDs.Insert(id)
} else {
printer.VV("removed %v/%v\n", restic.SnapshotFile, id)
}
@@ -331,6 +337,10 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
}
}
if len(failedSnIDs) > 0 {
return ErrFailedToRemoveOneOrMoreSnapshots
}
if len(removeSnIDs) > 0 && opts.Prune {
if opts.DryRun {
printer.P("%d snapshots would be removed, running prune dry run\n", len(removeSnIDs))

View File

@@ -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)
}

View File

@@ -5,6 +5,7 @@ package main
import (
"context"
"fmt"
"os"
"strings"
"time"
@@ -148,9 +149,11 @@ func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args
return err
}
fuseMountName := fmt.Sprintf("restic:%s", repo.Config().ID[:10])
mountOptions := []systemFuse.MountOption{
systemFuse.ReadOnly(),
systemFuse.FSName("restic"),
systemFuse.FSName(fuseMountName),
systemFuse.MaxReadahead(128 * 1024),
}

View File

@@ -46,7 +46,7 @@ import (
// to a missing backend storage location or config file
var ErrNoRepository = errors.New("repository does not exist")
var version = "0.18.0"
var version = "0.18.0-dev (compiled manually)"
// TimeFormat is the format used for all timestamps printed by restic.
const TimeFormat = "2006-01-02 15:04:05"
@@ -114,7 +114,7 @@ func (opts *GlobalOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.InsecureNoPassword, "insecure-no-password", false, "use an empty password for the repository, must be passed to every restic command (insecure)")
f.BoolVar(&opts.InsecureTLS, "insecure-tls", false, "skip TLS certificate verification when connecting to the repository (insecure)")
f.BoolVar(&opts.CleanupCache, "cleanup-cache", false, "auto remove old cache directories")
f.Var(&opts.Compression, "compression", "compression mode (only available for repository format version 2), one of (auto|off|max) (default: $RESTIC_COMPRESSION)")
f.Var(&opts.Compression, "compression", "compression mode (only available for repository format version 2), one of (auto|off|fastest|better|max) (default: $RESTIC_COMPRESSION)")
f.BoolVar(&opts.NoExtraVerify, "no-extra-verify", false, "skip additional verification of data before upload (see documentation)")
f.IntVar(&opts.Limits.UploadKb, "limit-upload", 0, "limits uploads to a maximum `rate` in KiB/s. (default: unlimited)")
f.IntVar(&opts.Limits.DownloadKb, "limit-download", 0, "limits downloads to a maximum `rate` in KiB/s. (default: unlimited)")

View File

@@ -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())
}

View File

@@ -206,6 +206,8 @@ func main() {
exitCode = 0
case err == ErrInvalidSourceData:
exitCode = 3
case errors.Is(err, ErrFailedToRemoveOneOrMoreSnapshots):
exitCode = 3
case errors.Is(err, ErrNoRepository):
exitCode = 10
case restic.IsAlreadyLocked(err):

View File

@@ -74,6 +74,16 @@ avoid any conflicts:
$ dnf copr remove copart/restic
Gentoo Linux
============
On `Gentoo Linux <https://www.gentoo.org/>`__, 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 <https://scoop.sh/>`__:
restic can be installed using either `Scoop <https://scoop.sh/>`__ or `WinGet <https://learn.microsoft.com/en-us/windows/package-manager/>`__.
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
****************

View File

@@ -349,7 +349,7 @@ Wasabi
S3 storage from `Wasabi <https://wasabi.com>`__ can be used as follows.
- Determine the correct Wasabi service URL for your bucket `here <https://wasabi-support.zendesk.com/hc/en-us/articles/360015106031-What-are-the-service-URLs-for-Wasabi-s-different-regions->`__.
- Determine the correct Wasabi service URL for your bucket `here <https://docs.wasabi.com/v1/docs/what-are-the-service-urls-for-wasabi-s-different-storage-regions>`__.
- 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.

View File

@@ -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``

View File

@@ -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

View File

@@ -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 <backend-name>.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
@@ -54,11 +55,10 @@ Compression
===========
For a repository using at least repository format version 2, you can configure how data
is compressed with the option ``--compression``. It can be set to ``auto`` (the default,
which will compress very fast), ``max`` (which will trade backup speed and CPU usage for
slightly better compression), or ``off`` (which disables compression). Each setting is
only applied for the single run of restic. The option can also be set via the environment
variable ``RESTIC_COMPRESSION``.
is compressed with the option ``--compression``. It can be set to ``off``, ``fastest``,
``auto`` (default), ``better``, or ``max``. Each setting uses more CPU but less bandwidth
and storage space. This setting is only applied for the single run of restic, but can also be
set via the environment variable ``RESTIC_COMPRESSION``.
Data Verification

View File

@@ -353,3 +353,72 @@ system.
root@a3e580b6369d:/# sudo -u restic /home/restic/bin/restic --exclude={/dev,/media,/mnt,/proc,/run,/sys,/tmp,/var/tmp} -r /tmp backup /
***********************************************************
Back up to an internal repository server over an SSH tunnel
***********************************************************
Idea
====
The idea is to run `REST-server <https://github.com/restic/rest-server>`__ on
an internal host as the repository server and then back up to it from a remote
restic client through a reverse SSH tunnel.
With this approach, you do not need to publicly expose the repository server
to which the backups are sent, as the restic client can instead connect to it
through the SSH tunnel.
An example use case for this method would be to create backups of a server,
e.g. a VPS in the cloud, to a repository stored on your local computer.
Running a local repository server
=================================
On the internal host, download and run the latest `release <https://github.com/restic/rest-server/releases>`__
of REST-server to act as the repository server. In this example we are using
the ``--no-auth`` option to not require authentication when connecting to it:
.. code-block:: console
rest-server --path /path/to/repo --no-auth
.. note:: REST-server by default listens on all network interfaces and port
``8000``.
Creating a reverse SSH tunnel
=============================
On the repository server (the internal host), use ``ssh -R`` to create what's
called a "reverse" SSH tunnel that listens for connections on the *remote* side
and forwards these back through the tunnel to the *local* side:
.. code-block:: console
ssh -R 8000:localhost:8000 user@server
.. note:: In this example, ``localhost`` refers to the local repository server,
and ``server`` refers to the remote system where restic is to be run.
Running restic on the remote system
===================================
Now that the SSH session and tunnel is established, run restic on the remote
system as usual, but with a repository URL that targets that system's side of
the SSH tunnel, in this example ``localhost:8000``.
This will make restic on the remote system connect to port ``8000`` on its
``localhost``, where the SSH tunnel is listening, after which the connection
is forwarded through the tunnel and finally reaches ``localhost:8000`` on the
local side where REST-server is listening and acting as the repository server.
To initialize the repository:
.. code-block:: console
restic -r rest:http://localhost:8000/ init
You can then use standard restic commands such as ``backup``, ``snapshots`` and
``restore`` with the same repository URL and other options as usual.
.. tip:: The tunnel will be active for the duration of the SSH session.

View File

@@ -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 <subcommand>`` 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 <https://graphviz.org/>`__ is in the PATH. Then,
run ``go tool pprof -http : cpu.pprof``.
************
@@ -121,14 +130,17 @@ Backward compatibility for backups is important so that our users are
always able to restore saved data. Therefore restic follows `Semantic
Versioning <https://semver.org>`__ to clearly define which versions are
compatible. The repository and data structures contained therein are
considered the "Public API" in the sense of Semantic Versioning. This
goes for all released versions of restic, this may not be the case for
the master branch.
considered the "Public API" in the sense of Semantic Versioning.
We guarantee backward compatibility of all repositories within one major
version; as long as we do not increment the major version, data can be
read and restored. We strive to be fully backward compatible to all
prior versions.
Once version 1.0.0 is released, we guarantee backward compatibility of
all repositories within one major version; as long as we do not
increment the major version, data can be read and restored. We strive
to be fully backward compatible to all prior versions.
During initial development (versions prior to 1.0.0), maintainers and
developers will do their utmost to keep backwards compatibility and
stability, although there might be breaking changes without increasing
the major version.
**********************
Building documentation

View File

@@ -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 <https://hub.docker.com/r/restic/builder>`__ as
``restic/builder``, the ``Dockerfile`` and instructions on how to build the
container can be found in the `GitHub repository
<https://github.com/restic/builder>`__
<https://github.com/restic/builder>`__.
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 \
<image-name>@<digest>
Replace `<tag>` with the Git tag of the release you're verifying, `<image-name>`
with the full name of the Docker image (including the registry), and `<digest>`
with the SHA256 digest of the image.
Replace `<image-name>` with the full name of the Docker image (including the registry),
and `<digest>` 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``.

View File

@@ -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:**

107
go.mod
View File

@@ -7,10 +7,10 @@ go 1.23.0
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
cloud.google.com/go/storage v1.56.1
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0
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/ncw/swift/v2 v2.0.3
github.com/peterbourgon/unixtransport v0.0.4
github.com/minio/minio-go/v7 v7.0.95
github.com/ncw/swift/v2 v2.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/xattr v0.4.10
github.com/pkg/sftp v1.13.9
github.com/pkg/xattr v0.4.12
github.com/restic/chunker v0.4.0
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/spf13/cobra v1.10.1
github.com/spf13/pflag v1.0.10
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
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
cel.dev/expr v0.24.0 // indirect
cloud.google.com/go v0.121.6 // 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.2 // 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.53.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // 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,38 +64,43 @@ 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.3.0 // 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.11 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/minio/crc64nvme v1.0.1 // indirect
github.com/minio/crc64nvme v1.0.2 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
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/tinylib/msgp v1.3.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-20250818200422-3122310a409c // 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
)

249
go.sum
View File

@@ -1,51 +1,51 @@
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.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
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/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/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.56.1 h1:n6gy+yLnHn0hTwBFzNn8zJ1kqWfR91wzdM8hjRF4wP0=
cloud.google.com/go/storage v1.56.1/go.mod h1:C9xuCZgFl3buo2HZU/1FncgvvOgTAs/rnh4gF4lMg0s=
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.19.0 h1:ci6Yd6nysBRLEodoziB6ah1+YOzZbZk+NYneoA6q+6E=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 h1:MhRfI58HblXzCtWEZCO0feHs8LweePB3s90r7WaR1KU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0/go.mod h1:okZ+ZURbArNdlJ+ptXoyHNuOETzOl1Oww19rm8I2WLA=
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.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
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/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=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
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.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
@@ -65,16 +65,15 @@ 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=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
@@ -96,17 +95,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.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
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 +125,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.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
github.com/klauspost/cpuid/v2 v2.2.11/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=
@@ -151,19 +152,21 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
github.com/minio/crc64nvme v1.0.2/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/ncw/swift/v2 v2.0.3 h1:8R9dmgFIWs+RiVlisCEfiQiik1hjuR0JnOkLxaP9ihg=
github.com/ncw/swift/v2 v2.0.3/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
github.com/ncw/swift/v2 v2.0.4 h1:hHWVFxn5/YaTWAASmn4qyq2p6OyP/Hm3vMLzkjEqR7w=
github.com/ncw/swift/v2 v2.0.4/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/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
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,18 +174,17 @@ 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/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA=
github.com/pkg/xattr v0.4.10/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
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.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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/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=
@@ -195,10 +197,13 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
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/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/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=
@@ -210,30 +215,34 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
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/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/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.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
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 +252,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 +275,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 +287,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 +308,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 +320,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 +333,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 +348,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-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
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=

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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)"`

View File

@@ -1,28 +0,0 @@
//go:build aix || solaris
// +build aix solaris
package util
import (
"os/exec"
"syscall"
"github.com/restic/restic/internal/errors"
)
func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
// run the command in its own process group so that SIGINT
// is not sent to it.
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
// start the process
err = cmd.Start()
if err != nil {
return nil, errors.Wrap(err, "cmd.Start")
}
bg = func() error { return nil }
return bg, nil
}

View File

@@ -1,5 +1,4 @@
//go:build !aix && !solaris && !windows
// +build !aix,!solaris,!windows
//go:build unix
package util
@@ -10,35 +9,45 @@ import (
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/ui/termstatus"
"golang.org/x/sys/unix"
)
func tcsetpgrp(fd int, pid int) error {
// IoctlSetPointerInt silently casts to int32 internally,
// so this assumes pid fits in 31 bits.
return unix.IoctlSetPointerInt(fd, unix.TIOCSPGRP, pid)
}
func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
// run the command in its own process group
// this ensures that sending ctrl-c to restic will not immediately stop the backend process.
cmd.SysProcAttr = &unix.SysProcAttr{
Setpgid: true,
}
// open the TTY, we need the file descriptor
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
debug.Log("unable to open tty: %v", err)
bg = func() error {
return nil
}
return bg, cmd.Start()
return startFallback(cmd)
}
// only move child process to foreground if restic is in the foreground
prev, err := termstatus.Tcgetpgrp(int(tty.Fd()))
if err != nil {
_ = tty.Close()
return nil, err
}
self := termstatus.Getpgrp()
if prev != self {
debug.Log("restic is not controlling the tty; err = %v", err)
if err := tty.Close(); err != nil {
return nil, err
}
return startFallback(cmd)
}
// Prevent getting suspended when interacting with the tty
signal.Ignore(unix.SIGTTIN)
signal.Ignore(unix.SIGTTOU)
// run the command in its own process group
cmd.SysProcAttr = &unix.SysProcAttr{
Setpgid: true,
}
// start the process
err = cmd.Start()
if err != nil {
@@ -47,8 +56,7 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
}
// move the command's process group into the foreground
prev := unix.Getpgrp()
err = tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
err = termstatus.Tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
if err != nil {
_ = tty.Close()
return nil, err
@@ -59,7 +67,7 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
signal.Reset(unix.SIGTTOU)
// reset the foreground process group
err = tcsetpgrp(int(tty.Fd()), prev)
err = termstatus.Tcsetpgrp(int(tty.Fd()), prev)
if err != nil {
_ = tty.Close()
return err
@@ -70,3 +78,11 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
return bg, nil
}
func startFallback(cmd *exec.Cmd) (bg func() error, err error) {
bg = func() error {
return nil
}
return bg, cmd.Start()
}

View File

@@ -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.

View File

@@ -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())
})
}
}

View File

@@ -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)

View File

@@ -73,7 +73,9 @@ const (
CompressionAuto CompressionMode = 0
CompressionOff CompressionMode = 1
CompressionMax CompressionMode = 2
CompressionInvalid CompressionMode = 3
CompressionFastest CompressionMode = 3
CompressionBetter CompressionMode = 4
CompressionInvalid CompressionMode = 5
)
// Set implements the method needed for pflag command flag parsing.
@@ -85,9 +87,13 @@ func (c *CompressionMode) Set(s string) error {
*c = CompressionOff
case "max":
*c = CompressionMax
case "fastest":
*c = CompressionFastest
case "better":
*c = CompressionBetter
default:
*c = CompressionInvalid
return fmt.Errorf("invalid compression mode %q, must be one of (auto|off|max)", s)
return fmt.Errorf("invalid compression mode %q, must be one of (auto|off|fastest|better|max)", s)
}
return nil
@@ -101,6 +107,10 @@ func (c *CompressionMode) String() string {
return "off"
case CompressionMax:
return "max"
case CompressionFastest:
return "fastest"
case CompressionBetter:
return "better"
default:
return "invalid"
}
@@ -305,9 +315,17 @@ func (r *Repository) loadBlob(ctx context.Context, blobs []restic.PackedBlob, bu
func (r *Repository) getZstdEncoder() *zstd.Encoder {
r.allocEnc.Do(func() {
level := zstd.SpeedDefault
if r.opts.Compression == CompressionMax {
var level zstd.EncoderLevel
switch r.opts.Compression {
case CompressionFastest:
level = zstd.SpeedFastest
case CompressionBetter:
level = zstd.SpeedBetterCompression
case CompressionMax:
level = zstd.SpeedBestCompression
default:
level = zstd.SpeedDefault
}
opts := []zstd.EOption{

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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{})

View File

@@ -1,27 +0,0 @@
package termstatus
import (
"github.com/restic/restic/internal/debug"
"golang.org/x/sys/unix"
)
// IsProcessBackground reports whether the current process is running in the
// background. fd must be a file descriptor for the terminal.
func IsProcessBackground(fd uintptr) bool {
bg, err := isProcessBackground(fd)
if err != nil {
debug.Log("Can't check if we are in the background. Using default behaviour. Error: %s\n", err.Error())
return false
}
return bg
}
func isProcessBackground(fd uintptr) (bool, error) {
// We need to use IoctlGetUint32 here, because pid_t is 32-bit even on
// 64-bit Linux. IoctlGetInt doesn't work on big-endian platforms:
// https://github.com/golang/go/issues/45585
// https://github.com/golang/go/issues/60429
pid, err := unix.IoctlGetUint32(int(fd), unix.TIOCGPGRP)
return int(pid) != unix.Getpgrp(), err
}

View File

@@ -0,0 +1,24 @@
//go:build unix
package termstatus
import "github.com/restic/restic/internal/debug"
// IsProcessBackground reports whether the current process is running in the
// background. fd must be a file descriptor for the terminal.
func IsProcessBackground(fd uintptr) bool {
bg, err := isProcessBackground(int(fd))
if err != nil {
debug.Log("Can't check if we are in the background. Using default behaviour. Error: %s\n", err.Error())
return false
}
return bg
}
func isProcessBackground(fd int) (bg bool, err error) {
pgid, err := Tcgetpgrp(fd)
if err != nil {
return false, err
}
return pgid != Getpgrp(), nil
}

View File

@@ -1,3 +1,5 @@
//go:build unix
package termstatus
import (
@@ -13,7 +15,7 @@ func TestIsProcessBackground(t *testing.T) {
t.Skipf("can't open terminal: %v", err)
}
_, err = isProcessBackground(tty.Fd())
_, err = isProcessBackground(int(tty.Fd()))
rtest.OK(t, err)
_ = tty.Close()

View File

@@ -1,6 +1,3 @@
//go:build !linux
// +build !linux
package termstatus
// IsProcessBackground reports whether the current process is running in the

View File

@@ -0,0 +1,8 @@
package termstatus
import "golang.org/x/sys/unix"
func Getpgrp() int {
pid, _ := unix.Getpgrp()
return pid
}

View File

@@ -0,0 +1,7 @@
//go:build unix && !solaris
package termstatus
import "golang.org/x/sys/unix"
func Getpgrp() int { return unix.Getpgrp() }

View File

@@ -0,0 +1,12 @@
package termstatus
import "golang.org/x/sys/unix"
func Tcgetpgrp(ttyfd int) (int, error) {
// We need to use IoctlGetUint32 here, because pid_t is 32-bit even on
// 64-bit Linux. IoctlGetInt doesn't work on big-endian platforms:
// https://github.com/golang/go/issues/45585
// https://github.com/golang/go/issues/60429
pid, err := unix.IoctlGetUint32(ttyfd, unix.TIOCGPGRP)
return int(pid), err
}

View File

@@ -0,0 +1,9 @@
//go:build unix && !linux
package termstatus
import "golang.org/x/sys/unix"
func Tcgetpgrp(ttyfd int) (int, error) {
return unix.IoctlGetInt(ttyfd, unix.TIOCGPGRP)
}

View File

@@ -0,0 +1,10 @@
package termstatus
import "golang.org/x/sys/unix"
func Tcsetpgrp(fd int, pid int) error {
// The second argument to IoctlSetPointerInt has type int on AIX,
// but the constant overflows 64-bit int, hence the two-step cast.
req := uint(unix.TIOCSPGRP)
return unix.IoctlSetPointerInt(fd, int(req), pid)
}

View File

@@ -0,0 +1,9 @@
//go:build unix && !aix
package termstatus
import "golang.org/x/sys/unix"
func Tcsetpgrp(fd int, pid int) error {
return unix.IoctlSetPointerInt(fd, unix.TIOCSPGRP, pid)
}

View File

@@ -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)

View File

@@ -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)
})
}
})