1
0
Fork 0
mirror of https://github.com/restic/restic.git synced 2025-03-16 00:00:05 +01:00

Merge remote-tracking branch 'origin/master' into add-smb-backend

This commit is contained in:
Aneesh Nireshwalia 2023-12-26 10:07:28 -07:00
commit 02381b8eeb
328 changed files with 6674 additions and 3095 deletions

View file

@ -32,23 +32,30 @@ Output of `restic version`
--------------------------
How did you run restic exactly?
-------------------------------
What backend/service did you use to store the repository?
---------------------------------------------------------
Problem description / Steps to reproduce
----------------------------------------
<!--
This section should include at least:
* A description of the problem you are having with restic.
* The complete command line and any environment variables you used to
configure restic's backend access. Make sure to replace sensitive values!
* The output of the commands, what restic prints gives may give us much
information to diagnose the problem!
* The more time you spend describing an easy way to reproduce the behavior (if
this is possible), the easier it is for the project developers to fix it!
-->
What backend/server/service did you use to store the repository?
----------------------------------------------------------------
Expected behavior
-----------------
@ -65,22 +72,12 @@ In this section, please try to concentrate on observations, so only describe
what you observed directly.
-->
Steps to reproduce the behavior
-------------------------------
<!--
The more time you spend describing an easy way to reproduce the behavior (if
this is possible), the easier it is for the project developers to fix it!
-->
Do you have any idea what may have caused this?
-----------------------------------------------
Do you have an idea how to solve the issue?
-------------------------------------------
<!--
Did something noteworthy happen on your system, Internet connection, backend services, etc?
-->
Did restic help you today? Did it make you happy in any way?

View file

@ -22,10 +22,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
uses: docker/login-action@3d58c274f17dffee475a5520cbe67f0a882c4dbb
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@ -42,10 +42,17 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
- name: Set up QEMU
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226
- name: Ensure consistent binaries
run: |
echo "removing git directory for consistency with release binaries"
rm -rf .git
# remove VCS information from release builds, keep VCS for nightly builds on master
if: github.ref != 'refs/heads/master'
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4

View file

@ -13,7 +13,7 @@ permissions:
contents: read
env:
latest_go: "1.20.x"
latest_go: "1.21.x"
GO111MODULE: on
jobs:
@ -23,18 +23,18 @@ jobs:
# list of jobs to run:
include:
- job_name: Windows
go: 1.20.x
go: 1.21.x
os: windows-latest
test_smb: true
- job_name: macOS
go: 1.20.x
go: 1.21.x
os: macOS-latest
test_fuse: false
test_smb: true
- job_name: Linux
go: 1.20.x
go: 1.21.x
os: ubuntu-latest
test_cloud_backends: true
test_fuse: true
@ -42,20 +42,20 @@ jobs:
check_changelog: true
- job_name: Linux (race)
go: 1.20.x
go: 1.21.x
os: ubuntu-latest
test_fuse: true
test_smb: true
test_opts: "-race"
- job_name: Linux
go: 1.19.x
go: 1.20.x
os: ubuntu-latest
test_fuse: true
test_smb: true
- job_name: Linux
go: 1.18.x
go: 1.19.x
os: ubuntu-latest
test_fuse: true
test_smb: true
@ -252,7 +252,7 @@ jobs:
if: matrix.os == 'windows-latest'
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build with build.go
run: |
@ -349,7 +349,7 @@ jobs:
go-version: ${{ env.latest_go }}
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Cross-compile for subset ${{ matrix.subset }}
run: |
@ -367,13 +367,13 @@ jobs:
go-version: ${{ env.latest_go }}
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.52.2
version: v1.55.2
args: --verbose --timeout 5m
# only run golangci-lint for pull requests, otherwise ALL hints get
@ -387,12 +387,27 @@ jobs:
go mod tidy
git diff --exit-code go.mod go.sum
analyze:
name: Analyze results
needs: [test, cross_compile, lint]
if: always()
permissions: # no need to access code
contents: none
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe
with:
jobs: ${{ toJSON(needs) }}
docker:
name: docker
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Docker meta
id: meta
@ -412,10 +427,10 @@ jobs:
type=sha
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Build and push
id: docker_build

22
.readthedocs.yaml Normal file
View file

@ -0,0 +1,22 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
# Build HTMLZip
formats:
- htmlzip
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: doc/conf.py
# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- requirements: doc/requirements.txt

File diff suppressed because it is too large Load diff

View file

@ -61,7 +61,7 @@ uploading it somewhere or post only the parts that are really relevant.
If restic gets stuck, please also include a stacktrace in the description.
On non-Windows systems, you can send a SIGQUIT signal to restic or press
`Ctrl-\` to achieve the same result. This causes restic to print a stacktrace
and then exit immediatelly. This will not damage your repository, however,
and then exit immediately. This will not damage your repository, however,
it might be necessary to manually clean up stale lock files using
`restic unlock`.

View file

@ -95,7 +95,7 @@ release. Instructions on how to do that are contained in the
News
----
You can follow the restic project on Twitter [@resticbackup](https://twitter.com/resticbackup) or by subscribing to
You can follow the restic project on Mastodon [@resticbackup](https://fosstodon.org/@restic) or by subscribing to
the [project blog](https://restic.net/blog/).
License

View file

@ -1 +1 @@
0.15.2
0.16.2

View file

@ -1,6 +1,6 @@
Bugfix: Don't abort the stats command when data blobs are missing
Runing the stats command in the blobs-per-file mode on a repository with
Running the stats command in the blobs-per-file mode on a repository with
missing data blobs previously resulted in a crash.
https://github.com/restic/restic/pull/2668

View file

@ -1,6 +1,6 @@
Bugfix: Mark repository files as read-only when using the local backend
Files stored in a local repository were marked as writeable on the
Files stored in a local repository were marked as writable on the
filesystem for non-Windows systems, which did not prevent accidental file
modifications outside of restic. In addition, the local backend did not work
with certain filesystems and network mounts which do not permit modifications

View file

@ -5,7 +5,7 @@ another process using an exclusive lock through a filesystem snapshot. Restic
was unable to backup those files before. This update enables backing up these
files.
This needs to be enabled explicitely using the --use-fs-snapshot option of the
This needs to be enabled explicitly using the --use-fs-snapshot option of the
backup command.
https://github.com/restic/restic/issues/340

View file

@ -2,7 +2,7 @@ Enhancement: Parallelize scan of snapshot content in `copy` and `prune`
The `copy` and `prune` commands used to traverse the directories of
snapshots one by one to find used data. This snapshot traversal is
now parallized which can speed up this step several times.
now parallelized which can speed up this step several times.
In addition the `check` command now reports how many snapshots have
already been processed.

View file

@ -3,7 +3,7 @@ Enhancement: Allow limiting IO concurrency for local and SFTP backend
Restic did not support limiting the IO concurrency / number of connections for
accessing repositories stored using the local or SFTP backends. The number of
connections is now limited as for other backends, and can be configured via the
the `-o local.connections=2` and `-o sftp.connections=5` options. This ensures
that restic does not overwhelm the backend with concurrent IO operations.
`-o local.connections=2` and `-o sftp.connections=5` options. This ensures that
restic does not overwhelm the backend with concurrent IO operations.
https://github.com/restic/restic/pull/3475

View file

@ -0,0 +1,7 @@
Enhancement: Sort snapshots by timestamp in `restic find`
The `find` command used to print snapshots in an arbitrary order. Restic now
prints snapshots sorted by timestamp.
https://github.com/restic/restic/issues/1495
https://github.com/restic/restic/pull/4409

View file

@ -4,12 +4,13 @@ The `rebuild-index` command has been renamed to `repair index`. The old name
will still work, but is deprecated.
When a snapshot was damaged, the only option up to now was to completely forget
the snapshot, even if only some unimportant file was damaged.
the snapshot, even if only some unimportant files in it were damaged and other
files were still fine.
We've added a `repair snapshots` command, which can repair snapshots by removing
damaged directories and missing files contents. Note that using this command
can lead to data loss! Please see the "Troubleshooting" section in the documentation
for more details.
Restic now has a `repair snapshots` command, which can salvage any non-damaged
files and parts of files in the snapshots by removing damaged directories and
missing file contents. Please note that the damaged data may still be lost
and see the "Troubleshooting" section in the documentation for more details.
https://github.com/restic/restic/issues/1759
https://github.com/restic/restic/issues/1714

View file

@ -0,0 +1,8 @@
Enhancement: Allow certificate paths to be passed through environment variables
Restic will now read paths to certificates from the environment variables
`RESTIC_CACERT` or `RESTIC_TLS_CLIENT_CERT` if `--cacert` or `--tls-client-cert`
are not specified.
https://github.com/restic/restic/issues/1926
https://github.com/restic/restic/pull/4384

View file

@ -1,9 +1,9 @@
Enhancement: Provide multi-platform Docker containers
Enhancement: Provide multi-platform Docker images
The official Docker containers are now built for the architectures linux/386,
The official Docker images are now built for the architectures linux/386,
linux/amd64, linux/arm and linux/arm64.
As an alternative to the Docker Hub, the Docker containers are now also
As an alternative to the Docker Hub, the Docker images are also
available on ghcr.io, the GitHub Container Registry.
https://github.com/restic/restic/issues/2359

View file

@ -1,10 +1,10 @@
Enhancement: Add support for non-global Azure clouds
Restic backups on Azure only supported storages using the global domain
The `azure` backend previously only supported storages using the global domain
`core.windows.net`. This meant that backups to other domains such as Azure
China (`core.chinacloudapi.cn') or Azure Germany (`core.cloudapi.de`) were
China (`core.chinacloudapi.cn`) or Azure Germany (`core.cloudapi.de`) were
not supported. Restic now allows overriding the global domain using the
environment variable `AZURE_ENDPOINT_SUFFIX'.
environment variable `AZURE_ENDPOINT_SUFFIX`.
https://github.com/restic/restic/issues/2468
https://github.com/restic/restic/pull/4387

View file

@ -0,0 +1,10 @@
Bugfix: Support "unlimited" in `forget --keep-*` options
Restic would previously forget snapshots that should have been kept when a
negative value was passed to the `--keep-*` options. Negative values are now
forbidden. To keep all snapshots, the special value `unlimited` is now
supported. For example, `--keep-monthly unlimited` will keep all monthly
snapshots.
https://github.com/restic/restic/issues/2565
https://github.com/restic/restic/pull/4234

View file

@ -0,0 +1,12 @@
Bugfix: Support non-UTF8 paths as symlink target
Earlier restic versions did not correctly `backup` and `restore` symlinks that
contain a non-UTF8 target. Note that this only affected systems that still use
a non-Unicode encoding for filesystem paths.
The repository format is now extended to add support for such symlinks. Please
note that snapshots must have been created with at least restic version 0.16.0
for `restore` to correctly handle non-UTF8 symlink targets when restoring them.
https://github.com/restic/restic/issues/3311
https://github.com/restic/restic/pull/3802

View file

@ -1,5 +1,9 @@
Enhancement: Reduce memory usage by up to 25%
The in-memory index has been optimized to be more garbage collection friendly.
Restic now defaults to `GOGC=50` to run the Go garbage collector more
frequently.
https://github.com/restic/restic/issues/3328
https://github.com/restic/restic/pull/4352
https://github.com/restic/restic/pull/4353

View file

@ -0,0 +1,11 @@
Enhancement: Improve accuracy of ETA displayed during backup
Restic's `backup` command displayed an ETA that did not adapt when the rate of
progress made during the backup changed during the course of the backup.
Restic now uses recent progress when computing the ETA. It is important to
realize that the estimate may still be wrong, because restic cannot predict
the future, but the hope is that the ETA will be more accurate in most cases.
https://github.com/restic/restic/issues/3397
https://github.com/restic/restic/pull/3563

View file

@ -1,8 +1,8 @@
Enhancement: Keep oldest snapshot when there are not enough snapshots
The `forget` command now additionally preserves the oldest snapshot if fewer
snapshots are kept than allowed by the `--keep-*` parameters. This maximizes
amount of history kept while the specified limits are not yet reached.
snapshots than allowed by the `--keep-*` parameters would otherwise be kept.
This maximizes the amount of history kept within the specified limits.
https://github.com/restic/restic/issues/3624
https://github.com/restic/restic/pull/4366

View file

@ -1,7 +1,7 @@
Enhancement: Add support for Managed / Worload Identity to azure backend
Enhancement: Add support for Managed / Workload Identity to `azure` backend
Restic now additionally supports authenticating to Azure using Workload
Identity or Managed Identity credentials which are automatically injected in
Identity or Managed Identity credentials, which are automatically injected in
several environments such as a managed Kubernetes cluster.
https://github.com/restic/restic/issues/3698

View file

@ -0,0 +1,22 @@
Enhancement: Support `<snapshot>:<subfolder>` syntax to select subfolders
Commands like `diff` or `restore` always worked with the full snapshot. This
did not allow comparing only a specific subfolder or only restoring that folder
(`restore --include subfolder` filters the restored files, but still creates the
directories included in `subfolder`).
The commands `diff`, `dump`, `ls` and `restore` now support the
`<snapshot>:<subfolder>` syntax, where `snapshot` is the ID of a snapshot (or
the string `latest`) and `subfolder` is a path within the snapshot. The
commands will then only work with the specified path of the snapshot. The
`subfolder` must be a path to a folder as returned by `ls`. Two examples:
`restic restore -t target latest:/some/path`
`restic diff 12345678:/some/path 90abcef:/some/path`
For debugging purposes, the `cat` command now supports `cat tree
<snapshot>:<subfolder>` to return the directory metadata for the given
subfolder.
https://github.com/restic/restic/issues/3871
https://github.com/restic/restic/pull/4334

View file

@ -0,0 +1,17 @@
Enhancement: Support `--group-by` for backup parent selection
Previously, the `backup` command by default selected the parent snapshot based
on the hostname and the backup targets. When the backup path list changed, the
`backup` command was unable to determine a suitable parent snapshot and had to
read all files again.
The new `--group-by` option for the `backup` command allows filtering snapshots
for the parent selection by `host`, `paths` and `tags`. It defaults to
`host,paths` which selects the latest snapshot with hostname and paths matching
those of the backup run. This matches the behavior of prior restic versions.
The new `--group-by` option should be set to the same value as passed to
`forget --group-by`.
https://github.com/restic/restic/issues/3941
https://github.com/restic/restic/pull/4081

View file

@ -0,0 +1,9 @@
Enhancement: Cancel current command if cache becomes unusable
If the cache directory was removed or ran out of space while restic was
running, this would previously cause further caching attempts to fail and
thereby drastically slow down the command execution. Now, the currently running
command is instead canceled.
https://github.com/restic/restic/issues/4130
https://github.com/restic/restic/pull/4166

View file

@ -0,0 +1,12 @@
Enhancement: Add `--human-readable` option to `ls` and `find` commands
Previously, when using the `-l` option with the `ls` and `find` commands, the
displayed size was always in bytes, without an option for a more human readable
format such as MiB or GiB.
The new `--human-readable` option will convert longer size values into more
human friendly values with an appropriate suffix depending on the output size.
For example, a size of `14680064` will be shown as `14.000 MiB`.
https://github.com/restic/restic/issues/4159
https://github.com/restic/restic/pull/4351

View file

@ -0,0 +1,8 @@
Enhancement: Include restic version in snapshot metadata
The restic version used to backup a snapshot is now included in its metadata
and shown when inspecting a snapshot using `restic cat snapshot <snapshotID>`
or `restic snapshots --json`.
https://github.com/restic/restic/issues/4188
https://github.com/restic/restic/pull/4378

View file

@ -0,0 +1,9 @@
Bugfix: Avoid lock refresh issues on slow network connections
On network connections with a low upload speed, backups and other operations
could fail with the error message `Fatal: failed to refresh lock in time`.
This has now been fixed by reworking the lock refresh handling.
https://github.com/restic/restic/issues/4199
https://github.com/restic/restic/pull/4304

View file

@ -2,7 +2,7 @@ Enhancement: Show progress bar during restore
The `restore` command now shows a progress report while restoring files.
Example: [0:42] 5.76% 23 files 12.98 MiB, total 3456 files 23.54 GiB
Example: `[0:42] 5.76% 23 files 12.98 MiB, total 3456 files 23.54 GiB`
JSON output is now also supported.

View file

@ -0,0 +1,11 @@
Bugfix: Improve lock refresh handling after standby
If the restic process was stopped or the host running restic entered standby
during a long running operation such as a backup, this previously resulted in
the operation failing with `Fatal: failed to refresh lock in time`.
This has now been fixed such that restic first checks whether it is safe to
continue the current operation and only throws an error if not.
https://github.com/restic/restic/issues/4274
https://github.com/restic/restic/pull/4374

View file

@ -0,0 +1,8 @@
Enhancement: Add `--retry-lock` option
This option allows specifying a duration for which restic will wait if the
repository is already locked.
https://github.com/restic/restic/issues/719
https://github.com/restic/restic/pull/2214
https://github.com/restic/restic/pull/4107

View file

@ -2,8 +2,6 @@ Change: Require Go 1.20 for Solaris builds
Building restic on Solaris now requires Go 1.20, as the library used to access
Azure uses the mmap syscall, which is only available on Solaris starting from
Go 1.20.
All other platforms continue to build with Go 1.18.
Go 1.20. All other platforms however continue to build with Go 1.18.
https://github.com/restic/restic/pull/4201

View file

@ -0,0 +1,6 @@
Enhancement: Add `jq` binary to Docker image
The Docker image now contains `jq`, which can be useful to process JSON data
output by restic.
https://github.com/restic/restic/pull/4220

View file

@ -0,0 +1,7 @@
Enhancement: Allow specifying region of new buckets in the `gs` backend
Previously, buckets used by the Google Cloud Storage backend would always get
created in the "us" region. It is now possible to specify the region where a
bucket should be created by using the `-o gs.region=us` option.
https://github.com/restic/restic/pull/4226

View file

@ -2,7 +2,7 @@ Bugfix: Correctly clean up status bar output of the `backup` command
Due to a regression in restic 0.15.2, the status bar of the `backup` command
could leave some output behind. This happened if filenames were printed that
are wider than the current terminal width. This has been fixed.
are wider than the current terminal width. This has now been fixed.
https://github.com/restic/restic/issues/4319
https://github.com/restic/restic/pull/4318

View file

@ -0,0 +1,8 @@
Bugfix: Ignore missing folders in `rest` backend
If a repository accessed via the REST backend was missing folders, then restic
would fail with an error while trying to list the data in the repository. This
has been now fixed.
https://github.com/restic/restic/pull/4400
https://github.com/restic/rest-server/issues/235

View file

@ -0,0 +1,9 @@
Enhancement: Automatically set `GOMAXPROCS` in resource-constrained containers
When running restic in a Linux container with CPU-usage limits, restic now
automatically adjusts `GOMAXPROCS`. This helps to reduce the memory consumption
on hosts with many CPU cores.
https://github.com/restic/restic/issues/4128
https://github.com/restic/restic/pull/4485
https://github.com/restic/restic/pull/4531

View file

@ -0,0 +1,8 @@
Bugfix: Make `key list` command honor `--no-lock`
The `key list` command now supports the `--no-lock` options. This allows
determining which keys a repo can be accessed by without the need for having
write access (e.g., read-only sftp access, filesystem snapshot).
https://github.com/restic/restic/issues/4513
https://github.com/restic/restic/pull/4514

View file

@ -0,0 +1,8 @@
Bugfix: Do not try to load password on command line autocomplete
The command line autocompletion previously tried to load the repository
password. This could cause the autocompletion not to work. Now, this step gets
skipped.
https://github.com/restic/restic/issues/4516
https://github.com/restic/restic/pull/4526

View file

@ -0,0 +1,22 @@
Bugfix: Update zstd library to fix possible data corruption at max. compression
In restic 0.16.0, backups where the compression level was set to `max` (using
`--compression max`) could in rare and very specific circumstances result in
data corruption due to a bug in the library used for compressing data.
Restic now uses the latest version of the library used to compress data, which
includes a fix for this issue. Please note that the `auto` compression level
(which restic uses by default) was never affected, and even if you used `max`
compression, chances of being affected by this issue were very small.
To check a repository for any corruption, run `restic check --read-data`. This
will download and verify the whole repository and can be used at any time to
completely verify the integrity of a repository. If the `check` command detects
anomalies, follow the suggested steps.
To simplify any needed repository repair and minimize data loss, there is also
a new and experimental `repair packs` command that salvages all valid data from
the affected pack files (see `restic help repair packs` for more information).
https://github.com/restic/restic/issues/4523
https://github.com/restic/restic/pull/4530

View file

@ -0,0 +1,7 @@
Enhancement: Show progress bar while loading the index
Restic did not provide any feedback while loading index files. Now, there is a
progress bar that shows the index loading progress.
https://github.com/restic/restic/issues/229
https://github.com/restic/restic/pull/4419

View file

@ -0,0 +1,11 @@
Enhancement: Allow setting REST password and username via environment variables
Previously, it was only possible to specify the REST-server username and
password in the repository URL, or by using the `--repository-file` option.
This meant it was not possible to use authentication in contexts where the
repository URL is stored in publicly accessible way.
Restic now allows setting the username and password using the
`RESTIC_REST_USERNAME` and `RESTIC_REST_PASSWORD` variables.
https://github.com/restic/restic/pull/4480

View file

@ -0,0 +1,7 @@
Enhancement: Include inode numbers in JSON output for `find` and `ls` commands
Restic used to omit the inode numbers in the JSON messages emitted for nodes by
the `ls` command as well as for matches by the `find` command. It now includes
those values whenever they are available.
https://github.com/restic/restic/pull/4511

View file

@ -0,0 +1,12 @@
Enhancement: Add config option to set SFTP command arguments
When using the `sftp` backend, scenarios where a custom identity file was
needed for the SSH connection, required the full command to be specified:
`-o sftp.command='ssh user@host:port -i /ssh/my_private_key -s sftp'`
Now, the `-o sftp.args=...` option can be passed to restic to specify
custom arguments for the SSH command executed by the SFTP backend.
This simplifies the above example to `-o sftp.args='-i /ssh/my_private_key'`.
https://github.com/restic/restic/pull/4519
https://github.com/restic/restic/issues/4241

View file

@ -0,0 +1,8 @@
Change: Update dependencies and require Go 1.19 or newer
We have updated all dependencies. Since some libraries require newer Go
standard library features, support for Go 1.18 has been dropped, which means
that restic now requires at least Go 1.19 to build.
https://github.com/restic/restic/pull/4532
https://github.com/restic/restic/pull/4533

View file

@ -0,0 +1,9 @@
Bugfix: Restore ARMv5 support for ARM binaries
The official release binaries for restic 0.16.1 were accidentally built to
require ARMv7. The build process is now updated to restore support for ARMv5.
Please note that restic 0.17.0 will drop support for ARMv5 and require at least
ARMv6.
https://github.com/restic/restic/issues/4540

View file

@ -0,0 +1,8 @@
Bugfix: Repair documentation build on Read the Docs
For restic 0.16.1, no documentation was available at
https://restic.readthedocs.io/ .
The documentation build process is now updated to work again.
https://github.com/restic/restic/pull/4545

View file

@ -3,7 +3,7 @@ Enhancement: Add local metadata cache
We've added a local cache for metadata so that restic doesn't need to load
all metadata (snapshots, indexes, ...) from the repo each time it starts. By
default the cache is active, but there's a new global option `--no-cache`
that can be used to disable the cache. By deafult, the cache a standard
that can be used to disable the cache. By default, the cache a standard
cache folder for the OS, which can be overridden with `--cache-dir`. The
cache will automatically populate, indexes and snapshots are saved as they
are loaded. Cache directories for repos that haven't been used recently can

View file

@ -1,6 +1,6 @@
Enhancement: Make `check` print `no errors found` explicitly
The `check` command now explicetly prints `No errors were found` when no errors
The `check` command now explicitly prints `No errors were found` when no errors
could be found.
https://github.com/restic/restic/pull/1319

View file

@ -1,4 +1,4 @@
Bugfix: Limit bandwith at the http.RoundTripper for HTTP based backends
Bugfix: Limit bandwidth at the http.RoundTripper for HTTP based backends
https://github.com/restic/restic/issues/1506
https://github.com/restic/restic/pull/1511

View file

@ -1,7 +1,7 @@
Bugfix: backup: Remove bandwidth display
This commit removes the bandwidth displayed during backup process. It is
misleading and seldomly correct, because it's neither the "read
misleading and seldom correct, because it's neither the "read
bandwidth" (only for the very first backup) nor the "upload bandwidth".
Many users are confused about (and rightly so), c.f. #1581, #1033, #1591

View file

@ -6,7 +6,7 @@ that means making a request (e.g. via HTTP) and returning an error when the
file already exists.
This is not accurate, the file could have been created between the HTTP request
testing for it, and when writing starts, so we've relaxed this requeriment,
testing for it, and when writing starts, so we've relaxed this requirement,
which saves one additional HTTP request per newly added file.
https://github.com/restic/restic/pull/1623

View file

@ -1,4 +1,4 @@
Enhancement: Allow keeping a time range of snaphots
Enhancement: Allow keeping a time range of snapshots
We've added the `--keep-within` option to the `forget` command. It instructs
restic to keep all snapshots within the given duration since the newest

View file

@ -1,7 +1,7 @@
Enhancement: Display reason why forget keeps snapshots
We've added a column to the list of snapshots `forget` keeps which details the
reasons to keep a particuliar snapshot. This makes debugging policies for
reasons to keep a particular snapshot. This makes debugging policies for
forget much easier. Please remember to always try things out with `--dry-run`!
https://github.com/restic/restic/pull/1876

View file

@ -9,7 +9,7 @@ file should be noticed, and the modified file will be backed up. The ctime check
will be disabled if the --ignore-inode flag was given.
If this change causes problems for you, please open an issue, and we can look in
to adding a seperate flag to disable just the ctime check.
to adding a separate flag to disable just the ctime check.
https://github.com/restic/restic/issues/2179
https://github.com/restic/restic/pull/2212

View file

@ -1,18 +1,21 @@
{{- range $changes := . }}{{ with $changes -}}
Changelog for restic {{ .Version }} ({{ .Date }})
=======================================
# Table of Contents
{{ range . -}}
* [Changelog for {{ .Version }}](#changelog-for-restic-{{ .Version | replace "." ""}}-{{ .Date | lower -}})
{{ end -}}
{{- range $changes := . }}{{ with $changes }}
# Changelog for restic {{ .Version }} ({{ .Date }})
The following sections list the changes in restic {{ .Version }} relevant to
restic users. The changes are ordered by importance.
Summary
-------
## Summary
{{ range $entry := .Entries }}{{ with $entry }}
* {{ .TypeShort }} #{{ .PrimaryID }}: {{ .Title }}
{{- end }}{{ end }}
Details
-------
## Details
{{ range $entry := .Entries }}{{ with $entry }}
* {{ .Type }} #{{ .PrimaryID }}: {{ .Title }}
{{ range $par := .Paragraphs }}
@ -27,6 +30,5 @@ Details
{{ range $url := .OtherURLs }}
{{ $url -}}
{{ end }}
{{ end }}{{ end }}
{{ end }}{{ end -}}
{{ end }}{{ end -}}

View file

@ -1,16 +1,17 @@
# The first line must start with Bugfix:, Enhancement: or Change:,
# including the colon. Use present tense. Remove lines starting with '#'
# from this template.
# including the colon. Use present tense and the imperative mood. Remove
# lines starting with '#' from this template.
Enhancement: Allow custom bar in the foo command
# Describe the problem in the past tense, the new behavior in the present
# tense. Mention the affected commands, backends, operating systems, etc.
# Focus on user-facing behavior, not the implementation.
# Use "Restic now ..." instead of "We have changed ...".
Restic foo always used the system-wide bar when deciding how to frob an
item in the baz backend. It now permits selecting the bar with --bar or
the environment variable RESTIC_BAR. The system-wide bar is still the
default.
item in the `baz` backend. It now permits selecting the bar with `--bar`
or the environment variable `RESTIC_BAR`. The system-wide bar is still
the default.
# The last section is a list of issue, PR and forum URLs.
# The first issue ID determines the filename for the changelog entry:

View file

@ -1,8 +0,0 @@
Enhancement: Certificates can be passed through environment variables
Restic will now read the paths to the certificates from the environment
variables `RESTIC_CACERT` or `RESTIC_TLS_CLIENT_CERT` if `--cacert` or
`--tls-client-cert` are not specified.
https://github.com/restic/restic/issues/1926
https://github.com/restic/restic/pull/4384

View file

@ -1,8 +0,0 @@
Bugfix: Restic forget --keep-* options now interpret "-1" as "forever"
Restic would forget snapshots that should have been kept when "-1" was
used as a value for --keep-* options. It now interprets "-1" as forever,
e.g. an option like --keep-monthly -1 will keep all monthly snapshots.
https://github.com/restic/restic/issues/2565
https://github.com/restic/restic/pull/4234

View file

@ -1,11 +0,0 @@
Enhancement: Improve the ETA displayed during backup
Restic's `backup` command displayed an ETA that did not adapt when the rate
of progress made during the backup changed during the course of the
backup. Restic now uses recent progress when computing the ETA. It is
important to realize that the estimate may still be wrong, because restic
cannot predict the future, but the hope is that the ETA will be more
accurate in most cases.
https://github.com/restic/restic/issues/3397
https://github.com/restic/restic/pull/3563

View file

@ -1,14 +0,0 @@
Enhancement: Support `--group-by` for backup parent selection
The backup command by default selected the parent snapshot based on the hostname
and the backup targets. When the backup path list changed, the backup command
was unable to determine a suitable parent snapshot and had to read all
files again.
The new `--group-by` option for the backup command allows filtering snapshots
for the parent selection by `host`, `paths` and `tags`. It defaults to
`host,paths` which selects the latest snapshot with hostname and paths matching
those of the backup run. It should be used consistently with `forget --group-by`.
https://github.com/restic/restic/issues/3941
https://github.com/restic/restic/pull/4081

View file

@ -1,8 +0,0 @@
Enhancement: `backup` includes restic version in snapshot metadata
The restic version used backup the snapshot is now included in its metadata.
The program version is shown when inspecting a snapshot using `restic cat
snapshot <snapshotID>` or `restic snapshots --json`.
https://github.com/restic/restic/issues/4188
https://github.com/restic/restic/pull/4378

View file

@ -0,0 +1,14 @@
Enhancement: Support reading backup from a program's standard output
When reading data from stdin, the `backup` command could not verify whether the
corresponding command completed successfully.
The `backup` command now supports starting an arbitrary command and sourcing
the backup content from its standard output. This enables restic to verify that
the command completes with exit code zero. A non-zero exit code causes the
backup to fail.
Example: `restic backup --stdin-from-command mysqldump [...]`
https://github.com/restic/restic/issues/4251
https://github.com/restic/restic/pull/4410

View file

@ -0,0 +1,8 @@
Change: Don't retry to load files that don't exist
Restic used to always retry to load files. It now only retries to load
files if they exist.
https://github.com/restic/restic/issues/4515
https://github.com/restic/restic/issues/1523
https://github.com/restic/restic/pull/4520

View file

@ -0,0 +1,6 @@
Change: Require at least ARMv6 for ARM binaries
The official release binaries of restic now require at least ARMv6 support for ARM platforms.
https://github.com/restic/restic/issues/4540
https://github.com/restic/restic/pull/4542

View file

@ -0,0 +1,7 @@
Enhancement: Add support for `--json` option to `version` command
Restic now supports outputting restic version and used go version, OS and
architecture via JSON when using the version command.
https://github.com/restic/restic/issues/4547
https://github.com/restic/restic/pull/4553

View file

@ -1,8 +0,0 @@
Enhancement: Add --retry-lock option
This option allows to specify a duration for which restic will wait if there
already exists a conflicting lock within the repository.
https://github.com/restic/restic/issues/719
https://github.com/restic/restic/pull/2214
https://github.com/restic/restic/pull/4107

View file

@ -1,7 +0,0 @@
Enhancement: Cancel current command if cache becomes unusable
If the cache directory was removed or ran out of space while restic was
running, this caused further caching attempts to fail and drastically slow down
the command execution. Now, the currently running command is canceled instead.
https://github.com/restic/restic/pull/4166

View file

@ -1,5 +0,0 @@
Enhancement: Add jq to container image
The Docker container image now contains jq which can be useful when restic outputs json data.
https://github.com/restic/restic/pull/4220

View file

@ -1,7 +0,0 @@
Enhancement: Allow specifying the region of new buckets in the gcs backend
Buckets used by the Google Cloud Storage backend would always get created in
the "us" region. It is now possible to specify the region, where a bucket
should get created.
https://github.com/restic/restic/pull/4226

View file

@ -1,5 +0,0 @@
Bugfix: Avoid lock refresh issues with slow network connections
On network connections with a low upload speed, restic could often fail backups and other operations with `Fatal: failed to refresh lock in time`. We've reworked the lock refresh to avoid this error.
https://github.com/restic/restic/pull/4304

View file

@ -1,6 +0,0 @@
Change: Building restic on AIX is temporarily unsupported
As the current version of the library used for the Azure backend does not
compile on AIX, there are currently no restic builds available for AIX.
https://github.com/restic/restic/pull/4365

View file

@ -0,0 +1,7 @@
Bugfix: Correct hardlink handling in `stats` command
If files on different devices had the same inode id, then the `stats` command
did not correctly calculate the snapshot size. This has been fixed.
https://github.com/restic/restic/pull/4503
https://forum.restic.net/t/possible-bug-in-stats/6461/8

View file

@ -0,0 +1,5 @@
Enhancement: Add `--new-host` and `--new-time` options to `rewrite` command
`restic rewrite` now allows rewriting the host and / or time metadata of a snapshot.
https://github.com/restic/restic/pull/4573

View file

@ -0,0 +1,7 @@
Enhancement: `mount` tests mountpoint existence before opening the repository
The restic `mount` command now checks for the existence of the
mountpoint before opening the repository, leading to quicker error
detection.
https://github.com/restic/restic/pull/4590

View file

@ -97,6 +97,7 @@ type BackupOptions struct {
ExcludeLargerThan string
Stdin bool
StdinFilename string
StdinCommand bool
Tags restic.TagLists
Host string
FilesFrom []string
@ -134,6 +135,7 @@ func init() {
f.StringVar(&backupOptions.ExcludeLargerThan, "exclude-larger-than", "", "max `size` of the files to be backed up (allowed suffixes: k/K, m/M, g/G, t/T)")
f.BoolVar(&backupOptions.Stdin, "stdin", false, "read backup from stdin")
f.StringVar(&backupOptions.StdinFilename, "stdin-filename", "stdin", "`filename` to use when reading from stdin")
f.BoolVar(&backupOptions.StdinCommand, "stdin-from-command", false, "execute command and store its stdout")
f.Var(&backupOptions.Tags, "tag", "add `tags` for the new snapshot in the format `tag[,tag,...]` (can be specified multiple times)")
f.UintVar(&backupOptions.ReadConcurrency, "read-concurrency", 0, "read `n` files concurrently (default: $RESTIC_READ_CONCURRENCY or 2)")
f.StringVarP(&backupOptions.Host, "host", "H", "", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag")
@ -287,7 +289,7 @@ func (opts BackupOptions) Check(gopts GlobalOptions, args []string) error {
}
}
if opts.Stdin {
if opts.Stdin || opts.StdinCommand {
if len(opts.FilesFrom) > 0 {
return errors.Fatal("--stdin and --files-from cannot be used together")
}
@ -298,7 +300,7 @@ func (opts BackupOptions) Check(gopts GlobalOptions, args []string) error {
return errors.Fatal("--stdin and --files-from-raw cannot be used together")
}
if len(args) > 0 {
if len(args) > 0 && !opts.StdinCommand {
return errors.Fatal("--stdin was specified and files/dirs were listed as arguments")
}
}
@ -366,7 +368,7 @@ func collectRejectFuncs(opts BackupOptions, targets []string) (fs []RejectFunc,
// collectTargets returns a list of target files/dirs from several sources.
func collectTargets(opts BackupOptions, args []string) (targets []string, err error) {
if opts.Stdin {
if opts.Stdin || opts.StdinCommand {
return nil, nil
}
@ -453,7 +455,7 @@ func findParentSnapshot(ctx context.Context, repo restic.Repository, opts Backup
f.Tags = []restic.TagList{opts.Tags.Flatten()}
}
sn, err := f.FindLatest(ctx, repo.Backend(), repo, snName)
sn, _, err := f.FindLatest(ctx, repo, repo, snName)
// Snapshot not found is ok if no explicit parent was set
if opts.Parent == "" && errors.Is(err, restic.ErrNoSnapshotFound) {
err = nil
@ -506,10 +508,13 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
if !gopts.JSON {
progressPrinter.V("lock repository")
}
lock, ctx, err := lockRepo(ctx, repo, gopts.RetryLock, gopts.JSON)
defer unlockRepo(lock)
if err != nil {
return err
if !opts.DryRun {
var lock *restic.Lock
lock, ctx, err = lockRepo(ctx, repo, gopts.RetryLock, gopts.JSON)
defer unlockRepo(lock)
if err != nil {
return err
}
}
// rejectByNameFuncs collect functions that can reject items from the backup based on path only
@ -543,7 +548,10 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
if !gopts.JSON {
progressPrinter.V("load index files")
}
err = repo.LoadIndex(ctx)
bar := newIndexTerminalProgress(gopts.Quiet, gopts.JSON, term)
err = repo.LoadIndex(ctx, bar)
if err != nil {
return err
}
@ -586,16 +594,24 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
defer localVss.DeleteSnapshots()
targetFS = localVss
}
if opts.Stdin {
if opts.Stdin || opts.StdinCommand {
if !gopts.JSON {
progressPrinter.V("read data from stdin")
}
filename := path.Join("/", opts.StdinFilename)
var source io.ReadCloser = os.Stdin
if opts.StdinCommand {
source, err = fs.NewCommandReader(ctx, args, globalOptions.stderr)
if err != nil {
return err
}
}
targetFS = &fs.Reader{
ModTime: timeStamp,
Name: filename,
Mode: 0644,
ReadCloser: os.Stdin,
ReadCloser: source,
}
targets = []string{filename}
}
@ -624,7 +640,13 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
success := true
arch.Error = func(item string, err error) error {
success = false
return progressReporter.Error(item, err)
reterr := progressReporter.Error(item, err)
// If we receive a fatal error during the execution of the snapshot,
// we abort the snapshot.
if reterr == nil && errors.IsFatal(err) {
reterr = err
}
return reterr
}
arch.CompleteItem = progressReporter.CompleteItem
arch.StartFile = progressReporter.StartFile

View file

@ -9,6 +9,7 @@ import (
"runtime"
"testing"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
@ -252,7 +253,7 @@ func TestBackupTreeLoadError(t *testing.T) {
r, err := OpenRepository(context.TODO(), env.gopts)
rtest.OK(t, err)
rtest.OK(t, r.LoadIndex(context.TODO()))
rtest.OK(t, r.LoadIndex(context.TODO(), nil))
treePacks := restic.NewIDSet()
r.Index().Each(context.TODO(), func(pb restic.PackedBlob) {
if pb.Type == restic.TreeBlob {
@ -265,7 +266,7 @@ func TestBackupTreeLoadError(t *testing.T) {
// delete the subdirectory pack first
for id := range treePacks {
rtest.OK(t, r.Backend().Remove(context.TODO(), restic.Handle{Type: restic.PackFile, Name: id.String()}))
rtest.OK(t, r.Backend().Remove(context.TODO(), backend.Handle{Type: restic.PackFile, Name: id.String()}))
}
testRunRebuildIndex(t, env.gopts)
// now the repo is missing the tree blob in the index; check should report this
@ -567,3 +568,72 @@ func linkEqual(source, dest []string) bool {
return true
}
func TestStdinFromCommand(t *testing.T) {
env, cleanup := withTestEnvironment(t)
defer cleanup()
testSetupBackupData(t, env)
opts := BackupOptions{
StdinCommand: true,
StdinFilename: "stdin",
}
testRunBackup(t, filepath.Dir(env.testdata), []string{"python", "-c", "import sys; print('something'); sys.exit(0)"}, opts, env.gopts)
testListSnapshots(t, env.gopts, 1)
testRunCheck(t, env.gopts)
}
func TestStdinFromCommandNoOutput(t *testing.T) {
env, cleanup := withTestEnvironment(t)
defer cleanup()
testSetupBackupData(t, env)
opts := BackupOptions{
StdinCommand: true,
StdinFilename: "stdin",
}
err := testRunBackupAssumeFailure(t, filepath.Dir(env.testdata), []string{"python", "-c", "import sys; sys.exit(0)"}, opts, env.gopts)
rtest.Assert(t, err != nil && err.Error() == "at least one source file could not be read", "No data error expected")
testListSnapshots(t, env.gopts, 1)
testRunCheck(t, env.gopts)
}
func TestStdinFromCommandFailExitCode(t *testing.T) {
env, cleanup := withTestEnvironment(t)
defer cleanup()
testSetupBackupData(t, env)
opts := BackupOptions{
StdinCommand: true,
StdinFilename: "stdin",
}
err := testRunBackupAssumeFailure(t, filepath.Dir(env.testdata), []string{"python", "-c", "import sys; print('test'); sys.exit(1)"}, opts, env.gopts)
rtest.Assert(t, err != nil, "Expected error while backing up")
testListSnapshots(t, env.gopts, 0)
testRunCheck(t, env.gopts)
}
func TestStdinFromCommandFailNoOutputAndExitCode(t *testing.T) {
env, cleanup := withTestEnvironment(t)
defer cleanup()
testSetupBackupData(t, env)
opts := BackupOptions{
StdinCommand: true,
StdinFilename: "stdin",
}
err := testRunBackupAssumeFailure(t, filepath.Dir(env.testdata), []string{"python", "-c", "import sys; sys.exit(1)"}, opts, env.gopts)
rtest.Assert(t, err != nil, "Expected error while backing up")
testListSnapshots(t, env.gopts, 0)
testRunCheck(t, env.gopts)
}

View file

@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
"strings"
"github.com/spf13/cobra"
@ -13,7 +14,7 @@ import (
)
var cmdCat = &cobra.Command{
Use: "cat [flags] [pack|blob|snapshot|index|key|masterkey|config|lock] ID",
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]",
Short: "Print internal objects to stdout",
Long: `
The "cat" command is used to print internal objects to stdout.
@ -33,9 +34,34 @@ func init() {
cmdRoot.AddCommand(cmdCat)
}
func validateCatArgs(args []string) error {
var allowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"}
if len(args) < 1 {
return errors.Fatal("type not specified")
}
validType := false
for _, v := range allowedCmds {
if v == args[0] {
validType = true
break
}
}
if !validType {
return errors.Fatalf("invalid type %q, must be one of [%s]", args[0], strings.Join(allowedCmds, "|"))
}
if args[0] != "masterkey" && args[0] != "config" && len(args) != 2 {
return errors.Fatal("ID not specified")
}
return nil
}
func runCat(ctx context.Context, gopts GlobalOptions, args []string) error {
if len(args) < 1 || (args[0] != "masterkey" && args[0] != "config" && len(args) != 2) {
return errors.Fatal("type or ID not specified")
if err := validateCatArgs(args); err != nil {
return err
}
repo, err := OpenRepository(ctx, gopts)
@ -55,7 +81,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string) error {
tpe := args[0]
var id restic.ID
if tpe != "masterkey" && tpe != "config" && tpe != "snapshot" {
if tpe != "masterkey" && tpe != "config" && tpe != "snapshot" && tpe != "tree" {
id, err = restic.ParseID(args[1])
if err != nil {
return errors.Fatalf("unable to parse ID: %v\n", err)
@ -80,7 +106,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string) error {
Println(string(buf))
return nil
case "snapshot":
sn, err := restic.FindSnapshot(ctx, repo.Backend(), repo, args[1])
sn, _, err := restic.FindSnapshot(ctx, repo, repo, args[1])
if err != nil {
return errors.Fatalf("could not find snapshot: %v\n", err)
}
@ -128,7 +154,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string) error {
return nil
case "pack":
h := restic.Handle{Type: restic.PackFile, Name: id.String()}
h := backend.Handle{Type: restic.PackFile, Name: id.String()}
buf, err := backend.LoadAll(ctx, nil, repo.Backend(), h)
if err != nil {
return err
@ -143,7 +169,8 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string) error {
return err
case "blob":
err = repo.LoadIndex(ctx)
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
err = repo.LoadIndex(ctx, bar)
if err != nil {
return err
}
@ -165,6 +192,30 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string) error {
return errors.Fatal("blob not found")
case "tree":
sn, subfolder, err := restic.FindSnapshot(ctx, repo, repo, args[1])
if err != nil {
return errors.Fatalf("could not find snapshot: %v\n", err)
}
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
err = repo.LoadIndex(ctx, bar)
if err != nil {
return err
}
sn.Tree, err = restic.FindTreeDirectory(ctx, repo, sn.Tree, subfolder)
if err != nil {
return err
}
buf, err := repo.LoadBlob(ctx, restic.TreeBlob, *sn.Tree, nil)
if err != nil {
return err
}
_, err = globalOptions.stdout.Write(buf)
return err
default:
return errors.Fatal("invalid type")
}

View file

@ -0,0 +1,30 @@
package main
import (
"strings"
"testing"
rtest "github.com/restic/restic/internal/test"
)
func TestCatArgsValidation(t *testing.T) {
for _, test := range []struct {
args []string
err string
}{
{[]string{}, "Fatal: type not specified"},
{[]string{"masterkey"}, ""},
{[]string{"invalid"}, `Fatal: invalid type "invalid"`},
{[]string{"snapshot"}, "Fatal: ID not specified"},
{[]string{"snapshot", "12345678"}, ""},
} {
t.Run("", func(t *testing.T) {
err := validateCatArgs(test.args)
if test.err == "" {
rtest.Assert(t, err == nil, "unexpected error %q", err)
} else {
rtest.Assert(t, strings.Contains(err.Error(), test.err), "unexpected error expected %q to contain %q", err, test.err)
}
})
}
}

View file

@ -226,7 +226,8 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
}
Verbosef("load indexes\n")
hints, errs := chkr.LoadIndex(ctx)
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
hints, errs := chkr.LoadIndex(ctx, bar)
errorsFound := false
suggestIndexRebuild := false
@ -329,11 +330,28 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
go chkr.ReadPacks(ctx, packs, p, errChan)
var salvagePacks restic.IDs
for err := range errChan {
errorsFound = true
Warnf("%v\n", err)
if err, ok := err.(*checker.ErrPackData); ok {
if strings.Contains(err.Error(), "wrong data returned, hash is") {
salvagePacks = append(salvagePacks, err.PackID)
}
}
}
p.Done()
if len(salvagePacks) > 0 {
Warnf("\nThe repository contains pack files with damaged blobs. These blobs must be removed to repair the repository. This can be done using the following commands:\n\n")
var strIds []string
for _, id := range salvagePacks {
strIds = append(strIds, id.String())
}
Warnf("RESTIC_FEATURES=repair-packs-v1 restic repair packs %v\nrestic repair snapshots --forget\n\n", strings.Join(strIds, " "))
Warnf("Corrupted blobs are either caused by hardware problems or bugs in restic. Please open an issue at https://github.com/restic/restic/issues/new/choose for further troubleshooting!\n")
}
}
switch {
@ -399,7 +417,7 @@ func selectPacksByBucket(allPacks map[restic.ID]int64, bucket, totalBuckets uint
return packs
}
// selectRandomPacksByPercentage selects the given percentage of packs which are randomly choosen.
// selectRandomPacksByPercentage selects the given percentage of packs which are randomly chosen.
func selectRandomPacksByPercentage(allPacks map[restic.ID]int64, percentage float64) map[restic.ID]int64 {
packCount := len(allPacks)
packsToCheck := int(float64(packCount) * (percentage / 100.0))

View file

@ -71,7 +71,7 @@ func TestSelectPacksByBucket(t *testing.T) {
var testPacks = make(map[restic.ID]int64)
for i := 1; i <= 10; i++ {
id := restic.NewRandomID()
// ensure relevant part of generated id is reproducable
// ensure relevant part of generated id is reproducible
id[0] = byte(i)
testPacks[id] = 0
}
@ -124,7 +124,7 @@ func TestSelectRandomPacksByPercentage(t *testing.T) {
}
func TestSelectNoRandomPacksByPercentage(t *testing.T) {
// that the a repository without pack files works
// that the repository without pack files works
var testPacks = make(map[restic.ID]int64)
selectedPacks := selectRandomPacksByPercentage(testPacks, 10.0)
rtest.Assert(t, len(selectedPacks) == 0, "Expected 0 selected packs")
@ -158,7 +158,7 @@ func TestSelectRandomPacksByFileSize(t *testing.T) {
}
func TestSelectNoRandomPacksByFileSize(t *testing.T) {
// that the a repository without pack files works
// that the repository without pack files works
var testPacks = make(map[restic.ID]int64)
selectedPacks := selectRandomPacksByFileSize(testPacks, 10, 500)
rtest.Assert(t, len(selectedPacks) == 0, "Expected 0 selected packs")

View file

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository"
@ -88,23 +87,24 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []
return err
}
srcSnapshotLister, err := backend.MemorizeList(ctx, srcRepo.Backend(), restic.SnapshotFile)
srcSnapshotLister, err := restic.MemorizeList(ctx, srcRepo, restic.SnapshotFile)
if err != nil {
return err
}
dstSnapshotLister, err := backend.MemorizeList(ctx, dstRepo.Backend(), restic.SnapshotFile)
dstSnapshotLister, err := restic.MemorizeList(ctx, dstRepo, restic.SnapshotFile)
if err != nil {
return err
}
debug.Log("Loading source index")
if err := srcRepo.LoadIndex(ctx); err != nil {
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
if err := srcRepo.LoadIndex(ctx, bar); err != nil {
return err
}
bar = newIndexProgress(gopts.Quiet, gopts.JSON)
debug.Log("Loading destination index")
if err := dstRepo.LoadIndex(ctx); err != nil {
if err := dstRepo.LoadIndex(ctx, bar); err != nil {
return err
}

View file

@ -78,7 +78,7 @@ func prettyPrintJSON(wr io.Writer, item interface{}) error {
}
func debugPrintSnapshots(ctx context.Context, repo *repository.Repository, wr io.Writer) error {
return restic.ForAllSnapshots(ctx, repo.Backend(), repo, nil, func(id restic.ID, snapshot *restic.Snapshot, err error) error {
return restic.ForAllSnapshots(ctx, repo, repo, nil, func(id restic.ID, snapshot *restic.Snapshot, err error) error {
if err != nil {
return err
}
@ -107,7 +107,7 @@ type Blob struct {
func printPacks(ctx context.Context, repo *repository.Repository, wr io.Writer) error {
var m sync.Mutex
return restic.ParallelList(ctx, repo.Backend(), restic.PackFile, repo.Connections(), func(ctx context.Context, id restic.ID, size int64) error {
return restic.ParallelList(ctx, repo, restic.PackFile, repo.Connections(), func(ctx context.Context, id restic.ID, size int64) error {
blobs, _, err := repo.ListPack(ctx, id, size)
if err != nil {
Warnf("error for pack %v: %v\n", id.Str(), err)
@ -134,7 +134,7 @@ func printPacks(ctx context.Context, repo *repository.Repository, wr io.Writer)
}
func dumpIndexes(ctx context.Context, repo restic.Repository, wr io.Writer) error {
return index.ForAllIndexes(ctx, repo, func(id restic.ID, idx *index.Index, oldFormat bool, err error) error {
return index.ForAllIndexes(ctx, repo, repo, func(id restic.ID, idx *index.Index, oldFormat bool, err error) error {
Printf("index_id: %v\n", id)
if err != nil {
return err
@ -290,7 +290,7 @@ func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte, by
})
err := wg.Wait()
if err != nil {
panic("all go rountines can only return nil")
panic("all go routines can only return nil")
}
if !found {
@ -321,7 +321,7 @@ func loadBlobs(ctx context.Context, repo restic.Repository, packID restic.ID, li
panic(err)
}
be := repo.Backend()
h := restic.Handle{
h := backend.Handle{
Name: packID.String(),
Type: restic.PackFile,
}
@ -447,7 +447,7 @@ func runDebugExamine(ctx context.Context, gopts GlobalOptions, args []string) er
for _, name := range args {
id, err := restic.ParseID(name)
if err != nil {
id, err = restic.Find(ctx, repo.Backend(), restic.PackFile, name)
id, err = restic.Find(ctx, repo, restic.PackFile, name)
if err != nil {
Warnf("error: %v\n", err)
continue
@ -469,7 +469,8 @@ func runDebugExamine(ctx context.Context, gopts GlobalOptions, args []string) er
}
}
err = repo.LoadIndex(ctx)
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
err = repo.LoadIndex(ctx, bar)
if err != nil {
return err
}
@ -489,7 +490,7 @@ func runDebugExamine(ctx context.Context, gopts GlobalOptions, args []string) er
func examinePack(ctx context.Context, repo restic.Repository, id restic.ID) error {
Printf("examine %v\n", id)
h := restic.Handle{
h := backend.Handle{
Type: restic.PackFile,
Name: id.String(),
}

View file

@ -7,7 +7,6 @@ import (
"reflect"
"sort"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic"
@ -16,7 +15,7 @@ import (
)
var cmdDiff = &cobra.Command{
Use: "diff [flags] snapshot-ID snapshot-ID",
Use: "diff [flags] snapshotID snapshotID",
Short: "Show differences between two snapshots",
Long: `
The "diff" command shows differences from the first to the second snapshot. The
@ -29,6 +28,10 @@ directory:
* M The file's content was modified
* T The type was changed, e.g. a file was made a symlink
To only compare files in specific subfolders, you can use the
"<snapshotID>:<subfolder>" syntax, where "subfolder" is a path within the
snapshot.
EXIT STATUS
===========
@ -54,12 +57,12 @@ func init() {
f.BoolVar(&diffOptions.ShowMetadata, "metadata", false, "print changes in metadata")
}
func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.Repository, desc string) (*restic.Snapshot, error) {
sn, err := restic.FindSnapshot(ctx, be, repo, desc)
func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.Repository, desc string) (*restic.Snapshot, string, error) {
sn, subfolder, err := restic.FindSnapshot(ctx, be, repo, desc)
if err != nil {
return nil, errors.Fatal(err.Error())
return nil, "", errors.Fatal(err.Error())
}
return sn, err
return sn, subfolder, err
}
// Comparer collects all things needed to compare two snapshots.
@ -342,16 +345,16 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args []
}
// cache snapshots listing
be, err := backend.MemorizeList(ctx, repo.Backend(), restic.SnapshotFile)
be, err := restic.MemorizeList(ctx, repo, restic.SnapshotFile)
if err != nil {
return err
}
sn1, err := loadSnapshot(ctx, be, repo, args[0])
sn1, subfolder1, err := loadSnapshot(ctx, be, repo, args[0])
if err != nil {
return err
}
sn2, err := loadSnapshot(ctx, be, repo, args[1])
sn2, subfolder2, err := loadSnapshot(ctx, be, repo, args[1])
if err != nil {
return err
}
@ -359,8 +362,8 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args []
if !gopts.JSON {
Verbosef("comparing snapshot %v to %v:\n\n", sn1.ID().Str(), sn2.ID().Str())
}
if err = repo.LoadIndex(ctx); err != nil {
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
if err = repo.LoadIndex(ctx, bar); err != nil {
return err
}
@ -372,6 +375,16 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args []
return errors.Errorf("snapshot %v has nil tree", sn2.ID().Str())
}
sn1.Tree, err = restic.FindTreeDirectory(ctx, repo, sn1.Tree, subfolder1)
if err != nil {
return err
}
sn2.Tree, err = restic.FindTreeDirectory(ctx, repo, sn2.Tree, subfolder2)
if err != nil {
return err
}
c := &Comparer{
repo: repo,
opts: diffOptions,

View file

@ -24,9 +24,13 @@ single file is selected, it prints its contents to stdout. Folders are output
as a tar (default) or zip file containing the contents of the specified folder.
Pass "/" as file name to dump the whole snapshot as an archive file.
The special snapshot "latest" can be used to use the latest snapshot in the
The special snapshotID "latest" can be used to use the latest snapshot in the
repository.
To include the folder content at the root of the archive, you can use the
"<snapshotID>:<subfolder>" syntax, where "subfolder" is a path within the
snapshot.
EXIT STATUS
===========
@ -139,16 +143,22 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []
}
}
sn, err := (&restic.SnapshotFilter{
sn, subfolder, err := (&restic.SnapshotFilter{
Hosts: opts.Hosts,
Paths: opts.Paths,
Tags: opts.Tags,
}).FindLatest(ctx, repo.Backend(), repo, snapshotIDString)
}).FindLatest(ctx, repo, repo, snapshotIDString)
if err != nil {
return errors.Fatalf("failed to find snapshot: %v", err)
}
err = repo.LoadIndex(ctx)
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
err = repo.LoadIndex(ctx, bar)
if err != nil {
return err
}
sn.Tree, err = restic.FindTreeDirectory(ctx, repo, sn.Tree, subfolder)
if err != nil {
return err
}

View file

@ -9,7 +9,6 @@ import (
"github.com/spf13/cobra"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter"
@ -51,6 +50,7 @@ type FindOptions struct {
PackID, ShowPackID bool
CaseInsensitive bool
ListLong bool
HumanReadable bool
restic.SnapshotFilter
}
@ -69,6 +69,7 @@ func init() {
f.BoolVar(&findOptions.ShowPackID, "show-pack-id", false, "display the pack-ID the blobs belong to (with --blob or --tree)")
f.BoolVarP(&findOptions.CaseInsensitive, "ignore-case", "i", false, "ignore case for pattern")
f.BoolVarP(&findOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
f.BoolVar(&findOptions.HumanReadable, "human-readable", false, "print sizes in human readable format")
initMultiSnapshotFilter(f, &findOptions.SnapshotFilter, true)
}
@ -104,12 +105,13 @@ func parseTime(str string) (time.Time, error) {
}
type statefulOutput struct {
ListLong bool
JSON bool
inuse bool
newsn *restic.Snapshot
oldsn *restic.Snapshot
hits int
ListLong bool
HumanReadable bool
JSON bool
inuse bool
newsn *restic.Snapshot
oldsn *restic.Snapshot
hits int
}
func (s *statefulOutput) PrintPatternJSON(path string, node *restic.Node) {
@ -123,7 +125,6 @@ func (s *statefulOutput) PrintPatternJSON(path string, node *restic.Node) {
// Make the following attributes disappear
Name byte `json:"name,omitempty"`
Inode byte `json:"inode,omitempty"`
ExtendedAttributes byte `json:"extended_attributes,omitempty"`
Device byte `json:"device,omitempty"`
Content byte `json:"content,omitempty"`
@ -164,7 +165,7 @@ func (s *statefulOutput) PrintPatternNormal(path string, node *restic.Node) {
s.oldsn = s.newsn
Verbosef("Found matching entries in snapshot %s from %s\n", s.oldsn.ID().Str(), s.oldsn.Time.Local().Format(TimeFormat))
}
Println(formatNode(path, node, s.ListLong))
Println(formatNode(path, node, s.ListLong, s.HumanReadable))
}
func (s *statefulOutput) PrintPattern(path string, node *restic.Node) {
@ -582,19 +583,19 @@ func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args []
}
}
snapshotLister, err := backend.MemorizeList(ctx, repo.Backend(), restic.SnapshotFile)
snapshotLister, err := restic.MemorizeList(ctx, repo, restic.SnapshotFile)
if err != nil {
return err
}
if err = repo.LoadIndex(ctx); err != nil {
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
if err = repo.LoadIndex(ctx, bar); err != nil {
return err
}
f := &Finder{
repo: repo,
pat: pat,
out: statefulOutput{ListLong: opts.ListLong, JSON: gopts.JSON},
out: statefulOutput{ListLong: opts.ListLong, HumanReadable: opts.HumanReadable, JSON: gopts.JSON},
ignoreTrees: restic.NewIDSet(),
}
@ -618,7 +619,16 @@ func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args []
}
}
var filteredSnapshots []*restic.Snapshot
for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, opts.Snapshots) {
filteredSnapshots = append(filteredSnapshots, sn)
}
sort.Slice(filteredSnapshots, func(i, j int) bool {
return filteredSnapshots[i].Time.Before(filteredSnapshots[j].Time)
})
for _, sn := range filteredSnapshots {
if f.blobIDs != nil || f.treeIDs != nil {
if err = f.findIDs(ctx, sn); err != nil && err.Error() != "OK" {
return err

View file

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"io"
"strconv"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic"
@ -36,14 +37,49 @@ Exit status is 0 if the command was successful, and non-zero if there was any er
},
}
type ForgetPolicyCount int
var ErrNegativePolicyCount = errors.New("negative values not allowed, use 'unlimited' instead")
func (c *ForgetPolicyCount) Set(s string) error {
switch s {
case "unlimited":
*c = -1
default:
val, err := strconv.ParseInt(s, 10, 0)
if err != nil {
return err
}
if val < 0 {
return ErrNegativePolicyCount
}
*c = ForgetPolicyCount(val)
}
return nil
}
func (c *ForgetPolicyCount) String() string {
switch *c {
case -1:
return "unlimited"
default:
return strconv.FormatInt(int64(*c), 10)
}
}
func (c *ForgetPolicyCount) Type() string {
return "n"
}
// ForgetOptions collects all options for the forget command.
type ForgetOptions struct {
Last int
Hourly int
Daily int
Weekly int
Monthly int
Yearly int
Last ForgetPolicyCount
Hourly ForgetPolicyCount
Daily ForgetPolicyCount
Weekly ForgetPolicyCount
Monthly ForgetPolicyCount
Yearly ForgetPolicyCount
Within restic.Duration
WithinHourly restic.Duration
WithinDaily restic.Duration
@ -67,12 +103,12 @@ func init() {
cmdRoot.AddCommand(cmdForget)
f := cmdForget.Flags()
f.IntVarP(&forgetOptions.Last, "keep-last", "l", 0, "keep the last `n` snapshots (use '-1' to keep all snapshots)")
f.IntVarP(&forgetOptions.Hourly, "keep-hourly", "H", 0, "keep the last `n` hourly snapshots (use '-1' to keep all hourly snapshots)")
f.IntVarP(&forgetOptions.Daily, "keep-daily", "d", 0, "keep the last `n` daily snapshots (use '-1' to keep all daily snapshots)")
f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots (use '-1' to keep all weekly snapshots)")
f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots (use '-1' to keep all monthly snapshots)")
f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots (use '-1' to keep all yearly snapshots)")
f.VarP(&forgetOptions.Last, "keep-last", "l", "keep the last `n` snapshots (use 'unlimited' to keep all snapshots)")
f.VarP(&forgetOptions.Hourly, "keep-hourly", "H", "keep the last `n` hourly snapshots (use 'unlimited' to keep all hourly snapshots)")
f.VarP(&forgetOptions.Daily, "keep-daily", "d", "keep the last `n` daily snapshots (use 'unlimited' to keep all daily snapshots)")
f.VarP(&forgetOptions.Weekly, "keep-weekly", "w", "keep the last `n` weekly snapshots (use 'unlimited' to keep all weekly snapshots)")
f.VarP(&forgetOptions.Monthly, "keep-monthly", "m", "keep the last `n` monthly snapshots (use 'unlimited' to keep all monthly snapshots)")
f.VarP(&forgetOptions.Yearly, "keep-yearly", "y", "keep the last `n` yearly snapshots (use 'unlimited' to keep all yearly snapshots)")
f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot")
f.VarP(&forgetOptions.WithinHourly, "keep-within-hourly", "", "keep hourly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot")
f.VarP(&forgetOptions.WithinDaily, "keep-within-daily", "", "keep daily snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot")
@ -147,7 +183,7 @@ func runForget(ctx context.Context, opts ForgetOptions, gopts GlobalOptions, arg
var snapshots restic.Snapshots
removeSnIDs := restic.NewIDSet()
for sn := range FindFilteredSnapshots(ctx, repo.Backend(), repo, &opts.SnapshotFilter, args) {
for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args) {
snapshots = append(snapshots, sn)
}
@ -165,12 +201,12 @@ func runForget(ctx context.Context, opts ForgetOptions, gopts GlobalOptions, arg
}
policy := restic.ExpirePolicy{
Last: opts.Last,
Hourly: opts.Hourly,
Daily: opts.Daily,
Weekly: opts.Weekly,
Monthly: opts.Monthly,
Yearly: opts.Yearly,
Last: int(opts.Last),
Hourly: int(opts.Hourly),
Daily: int(opts.Daily),
Weekly: int(opts.Weekly),
Monthly: int(opts.Monthly),
Yearly: int(opts.Yearly),
Within: opts.Within,
WithinHourly: opts.WithinHourly,
WithinDaily: opts.WithinDaily,

View file

@ -7,55 +7,84 @@ import (
rtest "github.com/restic/restic/internal/test"
)
func TestForgetPolicyValues(t *testing.T) {
testCases := []struct {
input string
value ForgetPolicyCount
err string
}{
{"0", ForgetPolicyCount(0), ""},
{"1", ForgetPolicyCount(1), ""},
{"unlimited", ForgetPolicyCount(-1), ""},
{"", ForgetPolicyCount(0), "strconv.ParseInt: parsing \"\": invalid syntax"},
{"-1", ForgetPolicyCount(0), ErrNegativePolicyCount.Error()},
{"abc", ForgetPolicyCount(0), "strconv.ParseInt: parsing \"abc\": invalid syntax"},
}
for _, testCase := range testCases {
t.Run("", func(t *testing.T) {
var count ForgetPolicyCount
err := count.Set(testCase.input)
if testCase.err != "" {
rtest.Assert(t, err != nil, "should have returned error for input %+v", testCase.input)
rtest.Equals(t, testCase.err, err.Error())
} else {
rtest.Assert(t, err == nil, "expected no error for input %+v, got %v", testCase.input, err)
rtest.Equals(t, testCase.value, count)
rtest.Equals(t, testCase.input, count.String())
}
})
}
}
func TestForgetOptionValues(t *testing.T) {
const negValErrorMsg = "Fatal: negative values other than -1 are not allowed for --keep-*"
const negDurationValErrorMsg = "Fatal: durations containing negative values are not allowed for --keep-within*"
testCases := []struct {
input ForgetOptions
expectsError bool
errorMsg string
input ForgetOptions
errorMsg string
}{
{ForgetOptions{Last: 1}, false, ""},
{ForgetOptions{Hourly: 1}, false, ""},
{ForgetOptions{Daily: 1}, false, ""},
{ForgetOptions{Weekly: 1}, false, ""},
{ForgetOptions{Monthly: 1}, false, ""},
{ForgetOptions{Yearly: 1}, false, ""},
{ForgetOptions{Last: 0}, false, ""},
{ForgetOptions{Hourly: 0}, false, ""},
{ForgetOptions{Daily: 0}, false, ""},
{ForgetOptions{Weekly: 0}, false, ""},
{ForgetOptions{Monthly: 0}, false, ""},
{ForgetOptions{Yearly: 0}, false, ""},
{ForgetOptions{Last: -1}, false, ""},
{ForgetOptions{Hourly: -1}, false, ""},
{ForgetOptions{Daily: -1}, false, ""},
{ForgetOptions{Weekly: -1}, false, ""},
{ForgetOptions{Monthly: -1}, false, ""},
{ForgetOptions{Yearly: -1}, false, ""},
{ForgetOptions{Last: -2}, true, negValErrorMsg},
{ForgetOptions{Hourly: -2}, true, negValErrorMsg},
{ForgetOptions{Daily: -2}, true, negValErrorMsg},
{ForgetOptions{Weekly: -2}, true, negValErrorMsg},
{ForgetOptions{Monthly: -2}, true, negValErrorMsg},
{ForgetOptions{Yearly: -2}, true, negValErrorMsg},
{ForgetOptions{Within: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""},
{ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""},
{ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""},
{ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""},
{ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("2y4m6d8h")}, false, ""},
{ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y4m6d8h")}, false, ""},
{ForgetOptions{Within: restic.ParseDurationOrPanic("-1y2m3d3h")}, true, negDurationValErrorMsg},
{ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y-2m3d3h")}, true, negDurationValErrorMsg},
{ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m-3d3h")}, true, negDurationValErrorMsg},
{ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d-3h")}, true, negDurationValErrorMsg},
{ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("-2y4m6d8h")}, true, negDurationValErrorMsg},
{ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y-4m6d8h")}, true, negDurationValErrorMsg},
{ForgetOptions{Last: 1}, ""},
{ForgetOptions{Hourly: 1}, ""},
{ForgetOptions{Daily: 1}, ""},
{ForgetOptions{Weekly: 1}, ""},
{ForgetOptions{Monthly: 1}, ""},
{ForgetOptions{Yearly: 1}, ""},
{ForgetOptions{Last: 0}, ""},
{ForgetOptions{Hourly: 0}, ""},
{ForgetOptions{Daily: 0}, ""},
{ForgetOptions{Weekly: 0}, ""},
{ForgetOptions{Monthly: 0}, ""},
{ForgetOptions{Yearly: 0}, ""},
{ForgetOptions{Last: -1}, ""},
{ForgetOptions{Hourly: -1}, ""},
{ForgetOptions{Daily: -1}, ""},
{ForgetOptions{Weekly: -1}, ""},
{ForgetOptions{Monthly: -1}, ""},
{ForgetOptions{Yearly: -1}, ""},
{ForgetOptions{Last: -2}, negValErrorMsg},
{ForgetOptions{Hourly: -2}, negValErrorMsg},
{ForgetOptions{Daily: -2}, negValErrorMsg},
{ForgetOptions{Weekly: -2}, negValErrorMsg},
{ForgetOptions{Monthly: -2}, negValErrorMsg},
{ForgetOptions{Yearly: -2}, negValErrorMsg},
{ForgetOptions{Within: restic.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("2y4m6d8h")}, ""},
{ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y4m6d8h")}, ""},
{ForgetOptions{Within: restic.ParseDurationOrPanic("-1y2m3d3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y-2m3d3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m-3d3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d-3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("-2y4m6d8h")}, negDurationValErrorMsg},
{ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y-4m6d8h")}, negDurationValErrorMsg},
}
for _, testCase := range testCases {
err := verifyForgetOptions(&testCase.input)
if testCase.expectsError {
if testCase.errorMsg != "" {
rtest.Assert(t, err != nil, "should have returned error for input %+v", testCase.input)
rtest.Equals(t, testCase.errorMsg, err.Error())
} else {

View file

@ -75,7 +75,7 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []
return err
}
repo, err := ReadRepo(gopts)
gopts.Repo, err = ReadRepo(gopts)
if err != nil {
return err
}
@ -87,7 +87,7 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []
return err
}
be, err := create(ctx, repo, gopts, gopts.extended)
be, err := create(ctx, gopts.Repo, gopts, gopts.extended)
if err != nil {
return errors.Fatalf("create repository at %s failed: %v\n", location.StripPassword(gopts.backends, gopts.Repo), err)
}

View file

@ -7,6 +7,7 @@ import (
"strings"
"sync"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
@ -59,7 +60,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions
var m sync.Mutex
var keys []keyInfo
err := restic.ParallelList(ctx, s.Backend(), restic.KeyFile, s.Connections(), func(ctx context.Context, id restic.ID, size int64) error {
err := restic.ParallelList(ctx, s, restic.KeyFile, s.Connections(), func(ctx context.Context, id restic.ID, size int64) error {
k, err := repository.LoadKey(ctx, s, id)
if err != nil {
Warnf("LoadKey() failed: %v\n", err)
@ -149,7 +150,7 @@ func deleteKey(ctx context.Context, repo *repository.Repository, id restic.ID) e
return errors.Fatal("refusing to remove key currently used to access repository")
}
h := restic.Handle{Type: restic.KeyFile, Name: id.String()}
h := backend.Handle{Type: restic.KeyFile, Name: id.String()}
err := repo.Backend().Remove(ctx, h)
if err != nil {
return err
@ -176,7 +177,7 @@ func changePassword(ctx context.Context, repo *repository.Repository, gopts Glob
return err
}
h := restic.Handle{Type: restic.KeyFile, Name: oldID.String()}
h := backend.Handle{Type: restic.KeyFile, Name: oldID.String()}
err = repo.Backend().Remove(ctx, h)
if err != nil {
return err
@ -193,7 +194,7 @@ func switchToNewKeyAndRemoveIfBroken(ctx context.Context, repo *repository.Repos
err := repo.SearchKey(ctx, pw, 0, key.ID().String())
if err != nil {
// the key is invalid, try to remove it
h := restic.Handle{Type: restic.KeyFile, Name: key.ID().String()}
h := backend.Handle{Type: restic.KeyFile, Name: key.ID().String()}
_ = repo.Backend().Remove(ctx, h)
return errors.Fatalf("failed to access repository with new key: %v", err)
}
@ -212,10 +213,13 @@ func runKey(ctx context.Context, gopts GlobalOptions, args []string) error {
switch args[0] {
case "list":
lock, ctx, err := lockRepo(ctx, repo, gopts.RetryLock, gopts.JSON)
defer unlockRepo(lock)
if err != nil {
return err
if !gopts.NoLock {
var lock *restic.Lock
lock, ctx, err = lockRepo(ctx, repo, gopts.RetryLock, gopts.JSON)
defer unlockRepo(lock)
if err != nil {
return err
}
}
return listKeys(ctx, repo, gopts)
@ -234,7 +238,7 @@ func runKey(ctx context.Context, gopts GlobalOptions, args []string) error {
return err
}
id, err := restic.Find(ctx, repo.Backend(), restic.KeyFile, args[1])
id, err := restic.Find(ctx, repo, restic.KeyFile, args[1])
if err != nil {
return err
}

View file

@ -6,8 +6,8 @@ import (
"regexp"
"testing"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
@ -110,11 +110,11 @@ func TestKeyAddRemove(t *testing.T) {
}
type emptySaveBackend struct {
restic.Backend
backend.Backend
}
func (b *emptySaveBackend) Save(ctx context.Context, h restic.Handle, _ restic.RewindReader) error {
return b.Backend.Save(ctx, h, restic.NewByteReader([]byte{}, nil))
func (b *emptySaveBackend) Save(ctx context.Context, h backend.Handle, _ backend.RewindReader) error {
return b.Backend.Save(ctx, h, backend.NewByteReader([]byte{}, nil))
}
func TestKeyProblems(t *testing.T) {
@ -122,7 +122,7 @@ func TestKeyProblems(t *testing.T) {
defer cleanup()
testRunInit(t, env.gopts)
env.gopts.backendTestHook = func(r restic.Backend) (restic.Backend, error) {
env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) {
return &emptySaveBackend{r}, nil
}

View file

@ -63,7 +63,7 @@ func runList(ctx context.Context, cmd *cobra.Command, gopts GlobalOptions, args
case "locks":
t = restic.LockFile
case "blobs":
return index.ForAllIndexes(ctx, repo, func(id restic.ID, idx *index.Index, oldFormat bool, err error) error {
return index.ForAllIndexes(ctx, repo, repo, func(id restic.ID, idx *index.Index, oldFormat bool, err error) error {
if err != nil {
return err
}

View file

@ -9,7 +9,6 @@ import (
"github.com/spf13/cobra"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
@ -50,7 +49,8 @@ Exit status is 0 if the command was successful, and non-zero if there was any er
type LsOptions struct {
ListLong bool
restic.SnapshotFilter
Recursive bool
Recursive bool
HumanReadable bool
}
var lsOptions LsOptions
@ -62,6 +62,7 @@ func init() {
initSingleSnapshotFilter(flags, &lsOptions.SnapshotFilter)
flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
flags.BoolVar(&lsOptions.HumanReadable, "human-readable", false, "print sizes in human readable format")
}
type lsSnapshot struct {
@ -85,6 +86,7 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
ModTime time.Time `json:"mtime,omitempty"`
AccessTime time.Time `json:"atime,omitempty"`
ChangeTime time.Time `json:"ctime,omitempty"`
Inode uint64 `json:"inode,omitempty"`
StructType string `json:"struct_type"` // "node"
size uint64 // Target for Size pointer.
@ -100,6 +102,7 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
ModTime: node.ModTime,
AccessTime: node.AccessTime,
ChangeTime: node.ChangeTime,
Inode: node.Inode,
StructType: "node",
}
// Always print size for regular files, even when empty,
@ -166,12 +169,13 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
return err
}
snapshotLister, err := backend.MemorizeList(ctx, repo.Backend(), restic.SnapshotFile)
snapshotLister, err := restic.MemorizeList(ctx, repo, restic.SnapshotFile)
if err != nil {
return err
}
if err = repo.LoadIndex(ctx); err != nil {
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
if err = repo.LoadIndex(ctx, bar); err != nil {
return err
}
@ -206,11 +210,11 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)
}
printNode = func(path string, node *restic.Node) {
Printf("%s\n", formatNode(path, node, lsOptions.ListLong))
Printf("%s\n", formatNode(path, node, lsOptions.ListLong, lsOptions.HumanReadable))
}
}
sn, err := (&restic.SnapshotFilter{
sn, subfolder, err := (&restic.SnapshotFilter{
Hosts: opts.Hosts,
Paths: opts.Paths,
Tags: opts.Tags,
@ -219,6 +223,11 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
return err
}
sn.Tree, err = restic.FindTreeDirectory(ctx, repo, sn.Tree, subfolder)
if err != nil {
return err
}
printSnapshot(sn)
err = walker.Walk(ctx, repo, *sn.Tree, nil, func(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {

View file

@ -113,6 +113,15 @@ func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args
return errors.Fatal("wrong number of parameters")
}
mountpoint := args[0]
// Check the existence of the mount point at the earliest stage to
// prevent unnecessary computations while opening the repository.
if _, err := resticfs.Stat(mountpoint); errors.Is(err, os.ErrNotExist) {
Verbosef("Mountpoint %s doesn't exist\n", mountpoint)
return err
}
debug.Log("start mount")
defer debug.Log("finish mount")
@ -130,17 +139,12 @@ func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args
}
}
err = repo.LoadIndex(ctx)
bar := newIndexProgress(gopts.Quiet, gopts.JSON)
err = repo.LoadIndex(ctx, bar)
if err != nil {
return err
}
mountpoint := args[0]
if _, err := resticfs.Stat(mountpoint); errors.Is(err, os.ErrNotExist) {
Verbosef("Mountpoint %s doesn't exist\n", mountpoint)
return err
}
mountOptions := []systemFuse.MountOption{
systemFuse.ReadOnly(),
systemFuse.FSName("restic"),

Some files were not shown because too many files have changed in this diff Show more