Compare commits

..

64 Commits

Author SHA1 Message Date
Alexander Neumann
7d0aa7f2e3 Add version for 0.18.1 2025-09-21 20:04:58 +02:00
Alexander Neumann
18f18b7f99 Generate CHANGELOG.md for 0.18.1 2025-09-21 20:03:56 +02:00
Alexander Neumann
426b71e3e5 Prepare changelog for 0.18.1 2025-09-21 20:03:56 +02:00
Michael Eischer
b7bb697cf7 Merge pull request #5513 from restic/more-polish-changelogs
doc: Nitpicks on changelogs
2025-09-17 20:38:19 +02:00
Michael Eischer
b12a638322 Merge pull request #5509 from restic/polish-changelogs
slightly polish changelogs
2025-09-17 20:37:46 +02:00
Leo R. Lundgren
4e0135e628 doc: Nitpicks on changelogs 2025-09-17 18:26:21 +02:00
Michael Eischer
9ef8e13102 slightly polish changelogs 2025-09-15 19:52:24 +02:00
Michael Eischer
4940e330c0 Merge pull request #5508 from restic/patch-release-cherrypicks
Patch release cherrypicks
2025-09-15 19:51:51 +02:00
Michael Eischer
3a63430b07 extend changelog 2025-09-15 19:34:25 +02:00
Michael Eischer
a5e814bd8d check: fix error reporting on download retry 2025-09-15 19:34:25 +02:00
Michael Eischer
398862c5c8 docs: sync compatibility section with website
This is no change in policy, just a more precise description of the
status quo.
2025-09-15 19:33:39 +02:00
Michael Eischer
b47c67fd90 update dependencies 2025-09-15 19:33:20 +02:00
Michael Eischer
6d7e37edce Merge pull request #5491 from MichaelEischer/patch-release-cherrypicks
Patch release cherrypicks
2025-09-06 22:32:40 +02:00
dependabot[bot]
4998fd68a7 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-06 22:07:22 +02:00
greatroar
06cc6017b8 internal/restic: Fix panic in ParseDuration
Fixes #5485. Includes test case by @MichaelEischer.
2025-09-06 22:03:12 +02:00
gregoster
37851827c5 EOPNOTSUPP can be returned if the filesystem does not support xattrs (#5344)
---------

Co-authored-by: Greg Oster <oster@netbsd.org>
2025-09-06 22:03:12 +02:00
Michael Eischer
b75f80ae5f backup: fix test on windows 2025-09-06 22:02:50 +02:00
Michael Eischer
31f87b6188 add changelog 2025-09-06 22:02:50 +02:00
Michael Eischer
b67b88a0c0 backup: test that parent directory errors can be correctly filtered 2025-09-06 22:02:50 +02:00
Michael Eischer
d57b01d6eb backup: test that missing parent directory is correctly handled 2025-09-06 22:02:50 +02:00
Michael Eischer
fc81df3f54 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-06 22:02:50 +02:00
Michael Eischer
73995b818a 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-06 22:02:50 +02:00
Michael Eischer
49abea6952 add changelog 2025-09-06 21:59:54 +02:00
Dominik Schulz
f18b8ad425 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-06 21:59:54 +02:00
dependabot[bot]
0a6296bfde 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-06 21:59:54 +02:00
dependabot[bot]
2403d1f139 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-06 21:59:54 +02:00
dependabot[bot]
86a453200a 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-06 21:59:54 +02:00
dependabot[bot]
518fbbcdc2 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-06 21:59:54 +02:00
y0n3d4
c62f523e6d Update 020_installation.rst removing command options
Removed command options: their use is a user choice
2025-09-06 21:59:11 +02:00
Michele Testa
91e9f65991 Update 020_installation.rst adding instruction for Gentoo Linux 2025-09-06 21:59:11 +02:00
rhhub
d839850ed4 docs: clarify ** must me between path separators 2025-09-06 21:59:11 +02:00
A Crutcher
ac051c3dcd doc: Correct Wasabi link 2025-09-06 21:59:11 +02:00
Michael Terry
20f472a67f backend/local: ignore chmod "not supported" errors 2025-09-06 21:59:11 +02:00
dependabot[bot]
7b986795de 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-09-06 21:59:11 +02:00
dependabot[bot]
4f03e03b2c 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-09-06 21:59:11 +02:00
Michael Eischer
242b607bf6 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-09-06 21:59:11 +02:00
Michael Eischer
22bbbf42f5 Fix release note typos 2025-09-06 21:59:11 +02:00
dependabot[bot]
3c8fc9d9bc 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-09-06 21:59:11 +02:00
dependabot[bot]
5070e62b18 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-09-06 21:59:11 +02:00
Patrick Wolf
d64bad1a90 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-09-06 21:54:41 +02:00
Michael Eischer
6bdca9a7d5 add changelog for --stdin-filename with/directory 2025-09-06 21:54:41 +02:00
Michael Eischer
91d582a667 backup: test subdirectories in stdin filenames work 2025-09-06 21:54:41 +02:00
Michael Eischer
ef1e137e7a fs/reader: return proper error on invalid filename 2025-09-06 21:54:41 +02:00
Michael Eischer
81ac49f59d fs/reader: test file not exist case 2025-09-06 21:54:41 +02:00
Michael Eischer
ba2b0b2cc7 fs/reader: use test helpers 2025-09-06 21:54:41 +02:00
Michael Eischer
37a4235e4d fs/reader: deduplicate test code 2025-09-06 21:54:41 +02:00
Michael Eischer
04898e41d1 fs/reader: fix open+stat handling 2025-09-06 21:54:41 +02:00
Michael Eischer
07e4a78e46 fs/reader: use modification time for file and directories
This ensures that a fixed input generates a fully deterministic output
file structure.
2025-09-06 21:54:41 +02:00
Michael Eischer
236f81758e 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-09-06 21:54:41 +02:00
Ilya Grigoriev
16850c61fa docs: when describing profiling, briefly explain .pprof files 2025-09-06 21:52:57 +02:00
Ilya Grigoriev
67a572fa0d 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-09-06 21:52:57 +02:00
Ilya Grigoriev
4686a12a2d 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-09-06 21:52:57 +02:00
dependabot[bot]
4dbed5f905 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-09-06 21:52:57 +02:00
dependabot[bot]
d708c5ea73 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-09-06 21:52:57 +02:00
Mark Lopez
ee0cb7d1aa docs: updated installation docs for Windows 2025-09-06 21:52:57 +02:00
dependabot[bot]
590dc82719 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-09-06 21:52:57 +02:00
Samuel Chambers
72d70d94f9 updated doc/faq.rst_commitsSquashed 2025-09-06 21:52:57 +02:00
Markus Hansmair
aaa48e765a doc: typo & minor rewording in 'Removing files from snapshots' 2025-09-06 21:50:54 +02:00
Michael Eischer
f61cf4a1e5 docs: fix typos in developer information (#5329) 2025-09-06 21:50:54 +02:00
Michael Eischer
a22b9d5735 update direct dependencies (#5340) 2025-09-06 21:50:54 +02:00
dependabot[bot]
e9ae67c968 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-09-06 21:50:54 +02:00
Mohammad Javad Naderi
1fe6fbc4b8 doc: fix typos 2025-09-06 21:50:54 +02:00
Gilbert Gilb's
3d4fb876f4 docs: fix unit for S3 restore timeout
"d" is not a valid unit.
2025-09-06 21:50:54 +02:00
Michael Eischer
5d182ed1ab forget: fix ignored RESTIC_HOST environment variable 2025-09-06 21:50:54 +02:00
37 changed files with 282 additions and 304 deletions

View File

@@ -1,5 +1,6 @@
# Table of Contents
* [Changelog for 0.18.1](#changelog-for-restic-0181-2025-09-21)
* [Changelog for 0.18.0](#changelog-for-restic-0180-2025-03-27)
* [Changelog for 0.17.3](#changelog-for-restic-0173-2024-11-08)
* [Changelog for 0.17.2](#changelog-for-restic-0172-2024-10-27)
@@ -39,6 +40,106 @@
* [Changelog for 0.6.0](#changelog-for-restic-060-2017-05-29)
# Changelog for restic 0.18.1 (2025-09-21)
The following sections list the changes in restic 0.18.1 relevant to
restic users. The changes are ordered by importance.
## Summary
* Fix #5324: Correctly handle `backup --stdin-filename` with directory paths
* Fix #5325: Accept `RESTIC_HOST` environment variable in `forget` command
* Fix #5342: Ignore "chmod not supported" errors when writing files
* Fix #5344: Ignore `EOPNOTSUPP` errors for extended attributes
* Fix #5421: Fix rare crash if directory is removed during backup
* Fix #5429: Stop retrying uploads when rest-server runs out of space
* Fix #5467: Improve handling of download retries in `check` command
## Details
* Bugfix #5324: Correctly handle `backup --stdin-filename` with directory paths
In restic 0.18.0, the `backup` command failed if a filename that includes at
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 now been fixed.
https://github.com/restic/restic/issues/5324
https://github.com/restic/restic/pull/5356
* Bugfix #5325: Accept `RESTIC_HOST` environment variable in `forget` command
The `forget` command did not use the host name from the `RESTIC_HOST`
environment variable when filtering snapshots. This has now been fixed.
https://github.com/restic/restic/issues/5325
https://github.com/restic/restic/pull/5327
* Bugfix #5342: 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
* Bugfix #5344: Ignore `EOPNOTSUPP` errors for extended attributes
Restic 0.18.0 added extended attribute support for NetBSD 10+, but not all
NetBSD filesystems support extended attributes. Other BSD systems can likewise
return `EOPNOTSUPP`, so restic now ignores these errors.
https://github.com/restic/restic/issues/5344
* Bugfix #5421: Fix rare crash if directory is removed during backup
In restic 0.18.0, the `backup` command could crash if a directory was removed
between reading its metadata and listing its directory content. This has now
been fixed.
https://github.com/restic/restic/pull/5421
* Bugfix #5429: Stop retrying uploads when rest-server runs out of space
When rest-server returns a `507 Insufficient Storage` error, it indicates that
no more storage capacity is available. Restic now correctly stops retrying
uploads in this case.
https://github.com/restic/restic/issues/5429
https://github.com/restic/restic/pull/5452
* Bugfix #5467: Improve handling of download retries in `check` command
In very rare cases, the `check` command could unnecessarily report repository
damage if the backend returned incomplete, corrupted data on the first download
try which is afterwards resolved by a download retry.
This could result in an error output like the following:
```
Load(<data/34567890ab>, 33918928, 0) returned error, retrying after 871.35598ms: readFull: unexpected EOF
Load(<data/34567890ab>, 33918928, 0) operation successful after 1 retries
check successful on second attempt, original error pack 34567890ab[...] contains 6 errors: [blob 12345678[...]: decrypting blob <data/12345678> from 34567890 failed: ciphertext verification failed ...]
[...]
Fatal: repository contains errors
```
This fix only applies to a very specific case where the log shows `operation
successful after 1 retries` followed by a `check successful on second attempt,
original error` that only reports `ciphertext verification failed` errors in the
pack file. If any other errors are reported in the pack file, then the
repository still has to be considered as damaged.
Now, only the check result of the last download retry is reported as intended.
https://github.com/restic/restic/issues/5467
https://github.com/restic/restic/pull/5495
# Changelog for restic 0.18.0 (2025-03-27)
The following sections list the changes in restic 0.18.0 relevant to
restic users. The changes are ordered by importance.

View File

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

View File

@@ -1,14 +1,14 @@
Bugfix: Correctly handle `backup --stdin-filename` with directories
Bugfix: Correctly handle `backup --stdin-filename` with directory paths
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,
at 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.
This has now been fixed.
https://github.com/restic/restic/issues/5324
https://github.com/restic/restic/pull/5356

View File

@@ -1,7 +1,7 @@
Bugfix: Correctly handle `RESTIC_HOST` in `forget` command
Bugfix: Accept `RESTIC_HOST` environment variable in `forget` command
The `forget` command did not use the host name from the `RESTIC_HOST`
environment variable. This has been fixed.
environment variable when filtering snapshots. This has now been fixed.
https://github.com/restic/restic/issues/5325
https://github.com/restic/restic/pull/5327

View File

@@ -1,6 +1,6 @@
Bugfix: Ignore "chmod not supported" errors when writing files
Restic 0.18.0 introduced a bug that caused "chmod xxx: operation not supported"
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.

View File

@@ -0,0 +1,7 @@
Bugfix: Ignore `EOPNOTSUPP` errors for extended attributes
Restic 0.18.0 added extended attribute support for NetBSD 10+, but not all
NetBSD filesystems support extended attributes. Other BSD systems can
likewise return `EOPNOTSUPP`, so restic now ignores these errors.
https://github.com/restic/restic/issues/5344

View File

@@ -0,0 +1,8 @@
Bugfix: Stop retrying uploads when rest-server runs out of space
When rest-server returns a `507 Insufficient Storage` error, it indicates
that no more storage capacity is available. Restic now correctly stops
retrying uploads in this case.
https://github.com/restic/restic/issues/5429
https://github.com/restic/restic/pull/5452

View File

@@ -0,0 +1,27 @@
Bugfix: Improve handling of download retries in `check` command
In very rare cases, the `check` command could unnecessarily report repository
damage if the backend returned incomplete, corrupted data on the first download
try which is afterwards resolved by a download retry.
This could result in an error output like the following:
```
Load(<data/34567890ab>, 33918928, 0) returned error, retrying after 871.35598ms: readFull: unexpected EOF
Load(<data/34567890ab>, 33918928, 0) operation successful after 1 retries
check successful on second attempt, original error pack 34567890ab[...] contains 6 errors: [blob 12345678[...]: decrypting blob <data/12345678> from 34567890 failed: ciphertext verification failed ...]
[...]
Fatal: repository contains errors
```
This fix only applies to a very specific case where the log shows
`operation successful after 1 retries` followed by a
`check successful on second attempt, original error` that only reports
`ciphertext verification failed` errors in the pack file. If any other errors
are reported in the pack file, then the repository still has to be considered
as damaged.
Now, only the check result of the last download retry is reported as intended.
https://github.com/restic/restic/issues/5467
https://github.com/restic/restic/pull/5495

View File

@@ -1,8 +1,7 @@
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.
In restic 0.18.0, the `backup` command could crash if a directory was removed
between reading its metadata and listing its directory content. This has now
been fixed.
https://github.com/restic/restic/pull/5421

View File

@@ -1,7 +0,0 @@
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

@@ -1,14 +0,0 @@
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

@@ -1,8 +0,0 @@
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

@@ -1,7 +0,0 @@
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

@@ -1,12 +0,0 @@
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

@@ -1,8 +0,0 @@
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

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

View File

@@ -41,7 +41,6 @@ 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.
@@ -63,7 +62,6 @@ 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 {
@@ -307,15 +305,12 @@ 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)
}
@@ -337,10 +332,6 @@ 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,7 +5,6 @@ package main
import (
"context"
"fmt"
"os"
"strings"
"time"
@@ -149,11 +148,9 @@ 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(fuseMountName),
systemFuse.FSName("restic"),
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-dev (compiled manually)"
var version = "0.18.1"
// 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|fastest|better|max) (default: $RESTIC_COMPRESSION)")
f.Var(&opts.Compression, "compression", "compression mode (only available for repository format version 2), one of (auto|off|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

@@ -206,8 +206,6 @@ 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

@@ -55,10 +55,11 @@ 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 ``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``.
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``.
Data Verification

View File

@@ -353,72 +353,3 @@ 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

@@ -0,0 +1,28 @@
//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,4 +1,5 @@
//go:build unix
//go:build !aix && !solaris && !windows
// +build !aix,!solaris,!windows
package util
@@ -9,45 +10,35 @@ 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 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,
}
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) {
// 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)
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
bg = func() error {
return nil
}
return startFallback(cmd)
return bg, cmd.Start()
}
// 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 {
@@ -56,7 +47,8 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
}
// move the command's process group into the foreground
err = termstatus.Tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
prev := unix.Getpgrp()
err = tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
if err != nil {
_ = tty.Close()
return nil, err
@@ -67,7 +59,7 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
signal.Reset(unix.SIGTTOU)
// reset the foreground process group
err = termstatus.Tcsetpgrp(int(tty.Fd()), prev)
err = tcsetpgrp(int(tty.Fd()), prev)
if err != nil {
_ = tty.Close()
return err
@@ -78,11 +70,3 @@ 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

@@ -293,7 +293,7 @@ type errorBackend struct {
func (b errorBackend) Load(ctx context.Context, h backend.Handle, length int, offset int64, consumer func(rd io.Reader) error) error {
return b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
if b.ProduceErrors {
return consumer(errorReadCloser{rd})
return consumer(errorReadCloser{Reader: rd})
}
return consumer(rd)
})
@@ -301,12 +301,21 @@ func (b errorBackend) Load(ctx context.Context, h backend.Handle, length int, of
type errorReadCloser struct {
io.Reader
shortenBy int
maxErrorOffset int // if 0, the error can be injected at any offset
}
func (erd errorReadCloser) Read(p []byte) (int, error) {
n, err := erd.Reader.Read(p)
if n > 0 {
induceError(p[:n])
maxOffset := n
if erd.maxErrorOffset > 0 {
maxOffset = min(erd.maxErrorOffset, maxOffset)
}
induceError(p[:maxOffset])
}
if n > erd.shortenBy {
n -= erd.shortenBy
}
return n, err
}
@@ -320,17 +329,26 @@ func induceError(data []byte) {
// errorOnceBackend randomly modifies data when reading a file for the first time.
type errorOnceBackend struct {
backend.Backend
m sync.Map
m sync.Map
shortenBy int
maxErrorOffset int
}
func (b *errorOnceBackend) Load(ctx context.Context, h backend.Handle, length int, offset int64, consumer func(rd io.Reader) error) error {
_, isRetry := b.m.LoadOrStore(h, struct{}{})
return b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
err := b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
if !isRetry && h.Type != restic.ConfigFile {
return consumer(errorReadCloser{rd})
return consumer(errorReadCloser{Reader: rd, shortenBy: b.shortenBy, maxErrorOffset: b.maxErrorOffset})
}
return consumer(rd)
})
if err == nil {
return nil
}
// retry if the consumer returned an error
return b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
return consumer(rd)
})
}
func TestCheckerModifiedData(t *testing.T) {
@@ -368,6 +386,15 @@ func TestCheckerModifiedData(t *testing.T) {
}
},
},
{
// ignore if a backend returns incomplete garbled data on the first try
"corruptPartialOnceBackend",
&errorOnceBackend{Backend: be, shortenBy: 10, maxErrorOffset: 100},
func() {},
func(t *testing.T, err error) {
test.Assert(t, err == nil, "unexpected error found, got %v", err)
},
},
} {
t.Run(test.name, func(t *testing.T) {
checkRepo := repository.TestOpenBackend(t, test.be)

View File

@@ -88,10 +88,14 @@ func checkPackInner(ctx context.Context, r *Repository, id restic.ID, blobs []re
// calculate hash on-the-fly while reading the pack and capture pack header
var hash restic.ID
var hdrBuf []byte
// must use a separate slice from `errs` here as we're only interested in the last retry
var blobErrors []error
h := backend.Handle{Type: backend.PackFile, Name: id.String()}
err := r.be.Load(ctx, h, int(size), 0, func(rd io.Reader) error {
hrd := hashing.NewReader(rd, sha256.New())
bufRd.Reset(hrd)
// reset blob errors for each retry
blobErrors = nil
it := newPackBlobIterator(id, newBufReader(bufRd), 0, blobs, r.Key(), dec)
for {
@@ -108,7 +112,7 @@ func checkPackInner(ctx context.Context, r *Repository, id restic.ID, blobs []re
debug.Log(" check blob %v: %v", val.Handle.ID, val.Handle)
if val.Err != nil {
debug.Log(" error verifying blob %v: %v", val.Handle.ID, val.Err)
errs = append(errs, errors.Errorf("blob %v: %v", val.Handle.ID, val.Err))
blobErrors = append(blobErrors, errors.Errorf("blob %v: %v", val.Handle.ID, val.Err))
}
}
@@ -134,6 +138,7 @@ func checkPackInner(ctx context.Context, r *Repository, id restic.ID, blobs []re
hash = restic.IDFromHash(hrd.Sum(nil))
return nil
})
errs = append(errs, blobErrors...)
if err != nil {
var e *partialReadError
isPartialReadError := errors.As(err, &e)

View File

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

View File

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

View File

@@ -0,0 +1,27 @@
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

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

View File

@@ -1,24 +0,0 @@
//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,8 +0,0 @@
package termstatus
import "golang.org/x/sys/unix"
func Getpgrp() int {
pid, _ := unix.Getpgrp()
return pid
}

View File

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

View File

@@ -1,12 +0,0 @@
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

@@ -1,9 +0,0 @@
//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

@@ -1,10 +0,0 @@
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

@@ -1,9 +0,0 @@
//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)
}