From 21ad357c107a98baec3541b5518da9023e9ea095 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 28 Jan 2023 20:19:07 +0100 Subject: [PATCH 01/16] add linux/riscv64 builds --- .github/workflows/tests.yml | 2 +- changelog/unreleased/pull-4180 | 5 +++++ helpers/build-release-binaries/main.go | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelog/unreleased/pull-4180 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ef827843..9c9555543 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -197,7 +197,7 @@ jobs: matrix: # run cross-compile in three batches parallel so the overall tests run faster targets: - - "linux/386 linux/amd64 linux/arm linux/arm64 linux/ppc64le linux/mips linux/mipsle linux/mips64 linux/mips64le linux/s390x" + - "linux/386 linux/amd64 linux/arm linux/arm64 linux/ppc64le linux/mips linux/mipsle linux/mips64 linux/mips64le linux/riscv64 linux/s390x" - "openbsd/386 openbsd/amd64 \ freebsd/386 freebsd/amd64 freebsd/arm \ diff --git a/changelog/unreleased/pull-4180 b/changelog/unreleased/pull-4180 new file mode 100644 index 000000000..ff43feb2b --- /dev/null +++ b/changelog/unreleased/pull-4180 @@ -0,0 +1,5 @@ +Enhancement: Add release binaries for riscv64 architecture on Linux + +We've added release binaries for riscv64 architecture on Linux. + +https://github.com/restic/restic/pull/4180 diff --git a/helpers/build-release-binaries/main.go b/helpers/build-release-binaries/main.go index 1662ada0b..0c0015f42 100644 --- a/helpers/build-release-binaries/main.go +++ b/helpers/build-release-binaries/main.go @@ -225,7 +225,7 @@ var defaultBuildTargets = map[string][]string{ "aix": {"ppc64"}, "darwin": {"amd64", "arm64"}, "freebsd": {"386", "amd64", "arm"}, - "linux": {"386", "amd64", "arm", "arm64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "s390x"}, + "linux": {"386", "amd64", "arm", "arm64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "riscv64", "s390x"}, "netbsd": {"386", "amd64"}, "openbsd": {"386", "amd64"}, "windows": {"386", "amd64"}, From 74f7dd0b38a8505943b5dd2ad938932c4f8d35a5 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Mon, 6 Feb 2023 22:11:21 +0100 Subject: [PATCH 02/16] Make help for --verbose less confusing The output is now ``` -v, --verbose be verbose (specify multiple times or a level using --verbose=n, max level/times is 2) ``` instead of ``` -v, --verbose n be verbose (specify multiple times or a level using --verbose=n, max level/times is 2) ``` --- cmd/restic/global.go | 3 ++- doc/manual_rest.rst | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/restic/global.go b/cmd/restic/global.go index 517388e8d..b32265275 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -112,7 +112,8 @@ func init() { f.StringVarP(&globalOptions.KeyHint, "key-hint", "", "", "`key` ID of key to try decrypting first (default: $RESTIC_KEY_HINT)") f.StringVarP(&globalOptions.PasswordCommand, "password-command", "", "", "shell `command` to obtain the repository password from (default: $RESTIC_PASSWORD_COMMAND)") f.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, "do not output comprehensive progress report") - f.CountVarP(&globalOptions.Verbose, "verbose", "v", "be verbose (specify multiple times or a level using --verbose=`n`, max level/times is 2)") + // use empty paremeter name as `-v, --verbose n` instead of the correct `--verbose=n` is confusing + f.CountVarP(&globalOptions.Verbose, "verbose", "v", "be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2)") f.BoolVar(&globalOptions.NoLock, "no-lock", false, "do not lock the repository, this allows some operations on read-only repositories") f.BoolVarP(&globalOptions.JSON, "json", "", false, "set output mode to JSON for commands that support it") f.StringVar(&globalOptions.CacheDir, "cache-dir", "", "set the cache `directory`. (default: use system default cache directory)") diff --git a/doc/manual_rest.rst b/doc/manual_rest.rst index 899a45688..97480db80 100644 --- a/doc/manual_rest.rst +++ b/doc/manual_rest.rst @@ -67,7 +67,7 @@ Usage help is available: -r, --repo repository repository to backup to or restore from (default: $RESTIC_REPOSITORY) --repository-file file file to read the repository location from (default: $RESTIC_REPOSITORY_FILE) --tls-client-cert file path to a file containing PEM encoded TLS client certificate and private key - -v, --verbose n be verbose (specify multiple times or a level using --verbose=n, max level/times is 2) + -v, --verbose be verbose (specify multiple times or a level using --verbose=n, max level/times is 2) Use "restic [command] --help" for more information about a command. @@ -142,7 +142,7 @@ command: -r, --repo repository repository to backup to or restore from (default: $RESTIC_REPOSITORY) --repository-file file file to read the repository location from (default: $RESTIC_REPOSITORY_FILE) --tls-client-cert file path to a file containing PEM encoded TLS client certificate and private key - -v, --verbose n be verbose (specify multiple times or a level using --verbose=n, max level/times is 2) + -v, --verbose be verbose (specify multiple times or a level using --verbose=n, max level/times is 2) Subcommands that support showing progress information such as ``backup``, ``check`` and ``prune`` will do so unless the quiet flag ``-q`` or From 97274ecabd26157881c4ff8f3bf062c3e7d51c14 Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Fri, 17 Feb 2023 16:13:46 +0100 Subject: [PATCH 03/16] cmd, restic: Refactor and fix snapshot filtering This turns snapshotFilterOptions from cmd into a restic.SnapshotFilter type and makes restic.FindFilteredSnapshot and FindFilteredSnapshots methods on that type. This fixes #4211 by ensuring that hosts and paths are named struct fields instead of unnamed function arguments in long lists of such. Timestamp limits are also included in the new type. To avoid too much pointer handling, the convention is that time zero means no limit. That's January 1st, year 1, 00:00 UTC, which is so unlikely a date that we can sacrifice it for simpler code. --- changelog/unreleased/issue-4211 | 8 ++++ cmd/restic/cmd_backup.go | 8 +++- cmd/restic/cmd_copy.go | 9 ++-- cmd/restic/cmd_dump.go | 10 ++-- cmd/restic/cmd_find.go | 6 +-- cmd/restic/cmd_forget.go | 6 +-- cmd/restic/cmd_ls.go | 10 ++-- cmd/restic/cmd_mount.go | 8 ++-- cmd/restic/cmd_restore.go | 10 ++-- cmd/restic/cmd_rewrite.go | 6 +-- cmd/restic/cmd_snapshots.go | 6 +-- cmd/restic/cmd_stats.go | 6 +-- cmd/restic/cmd_tag.go | 6 +-- cmd/restic/find.go | 30 +++++------- cmd/restic/integration_test.go | 4 +- internal/fuse/root.go | 4 +- internal/fuse/snapshots_dirstruct.go | 2 +- internal/restic/snapshot_find.go | 68 +++++++++++++++------------ internal/restic/snapshot_find_test.go | 18 +++---- 19 files changed, 126 insertions(+), 99 deletions(-) create mode 100644 changelog/unreleased/issue-4211 diff --git a/changelog/unreleased/issue-4211 b/changelog/unreleased/issue-4211 new file mode 100644 index 000000000..45b7aee83 --- /dev/null +++ b/changelog/unreleased/issue-4211 @@ -0,0 +1,8 @@ +Bugfix: Restic dump now interprets --host and --path correctly + +Restic dump previously confused its --host= and --path= +options: it looked for snapshots with paths called from hosts +called . It now treats the options as intended. + +https://github.com/restic/restic/issues/4211 +https://github.com/restic/restic/pull/4212 diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index e59f503db..ec901828b 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -439,7 +439,13 @@ func findParentSnapshot(ctx context.Context, repo restic.Repository, opts Backup if snName == "" { snName = "latest" } - sn, err := restic.FindFilteredSnapshot(ctx, repo.Backend(), repo, []string{opts.Host}, []restic.TagList{}, targets, &timeStampLimit, snName) + f := restic.SnapshotFilter{ + Hosts: []string{opts.Host}, + Paths: targets, + TimestampLimit: timeStampLimit, + } + + sn, err := f.FindLatest(ctx, repo.Backend(), repo, snName) // Snapshot not found is ok if no explicit parent was set if opts.Parent == "" && errors.Is(err, restic.ErrNoSnapshotFound) { err = nil diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index 14ab1917a..2f095972a 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -39,7 +39,7 @@ new destination repository using the "init" command. // CopyOptions bundles all options for the copy command. type CopyOptions struct { secondaryRepoOptions - snapshotFilterOptions + restic.SnapshotFilter } var copyOptions CopyOptions @@ -49,7 +49,7 @@ func init() { f := cmdCopy.Flags() initSecondaryRepoOptions(f, ©Options.secondaryRepoOptions, "destination", "to copy snapshots from") - initMultiSnapshotFilterOptions(f, ©Options.snapshotFilterOptions, true) + initMultiSnapshotFilter(f, ©Options.SnapshotFilter, true) } func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string) error { @@ -108,7 +108,7 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args [] } dstSnapshotByOriginal := make(map[restic.ID][]*restic.Snapshot) - for sn := range FindFilteredSnapshots(ctx, dstSnapshotLister, dstRepo, opts.Hosts, opts.Tags, opts.Paths, nil) { + for sn := range FindFilteredSnapshots(ctx, dstSnapshotLister, dstRepo, &opts.SnapshotFilter, nil) { if sn.Original != nil && !sn.Original.IsNull() { dstSnapshotByOriginal[*sn.Original] = append(dstSnapshotByOriginal[*sn.Original], sn) } @@ -119,8 +119,7 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args [] // remember already processed trees across all snapshots visitedTrees := restic.NewIDSet() - for sn := range FindFilteredSnapshots(ctx, srcSnapshotLister, srcRepo, opts.Hosts, opts.Tags, opts.Paths, args) { - + for sn := range FindFilteredSnapshots(ctx, srcSnapshotLister, srcRepo, &opts.SnapshotFilter, args) { // check whether the destination has a snapshot with the same persistent ID which has similar snapshot fields srcOriginal := *sn.ID() if sn.Original != nil { diff --git a/cmd/restic/cmd_dump.go b/cmd/restic/cmd_dump.go index a480b12f4..cda7b65b9 100644 --- a/cmd/restic/cmd_dump.go +++ b/cmd/restic/cmd_dump.go @@ -40,7 +40,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er // DumpOptions collects all options for the dump command. type DumpOptions struct { - snapshotFilterOptions + restic.SnapshotFilter Archive string } @@ -50,7 +50,7 @@ func init() { cmdRoot.AddCommand(cmdDump) flags := cmdDump.Flags() - initSingleSnapshotFilterOptions(flags, &dumpOptions.snapshotFilterOptions) + initSingleSnapshotFilter(flags, &dumpOptions.SnapshotFilter) flags.StringVarP(&dumpOptions.Archive, "archive", "a", "tar", "set archive `format` as \"tar\" or \"zip\"") } @@ -139,7 +139,11 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args [] } } - sn, err := restic.FindFilteredSnapshot(ctx, repo.Backend(), repo, opts.Paths, opts.Tags, opts.Hosts, nil, snapshotIDString) + sn, err := (&restic.SnapshotFilter{ + Hosts: opts.Hosts, + Paths: opts.Paths, + Tags: opts.Tags, + }).FindLatest(ctx, repo.Backend(), repo, snapshotIDString) if err != nil { return errors.Fatalf("failed to find snapshot: %v", err) } diff --git a/cmd/restic/cmd_find.go b/cmd/restic/cmd_find.go index 8e5f9b604..e5457c3be 100644 --- a/cmd/restic/cmd_find.go +++ b/cmd/restic/cmd_find.go @@ -51,7 +51,7 @@ type FindOptions struct { PackID, ShowPackID bool CaseInsensitive bool ListLong bool - snapshotFilterOptions + restic.SnapshotFilter } var findOptions FindOptions @@ -70,7 +70,7 @@ func init() { 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") - initMultiSnapshotFilterOptions(f, &findOptions.snapshotFilterOptions, true) + initMultiSnapshotFilter(f, &findOptions.SnapshotFilter, true) } type findPattern struct { @@ -618,7 +618,7 @@ func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args [] } } - for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, opts.Hosts, opts.Tags, opts.Paths, opts.Snapshots) { + for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, opts.Snapshots) { if f.blobIDs != nil || f.treeIDs != nil { if err = f.findIDs(ctx, sn); err != nil && err.Error() != "OK" { return err diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index 472b22b79..e4e44a368 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -52,7 +52,7 @@ type ForgetOptions struct { WithinYearly restic.Duration KeepTags restic.TagLists - snapshotFilterOptions + restic.SnapshotFilter Compact bool // Grouping @@ -81,7 +81,7 @@ func init() { f.VarP(&forgetOptions.WithinYearly, "keep-within-yearly", "", "keep yearly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)") - initMultiSnapshotFilterOptions(f, &forgetOptions.snapshotFilterOptions, false) + initMultiSnapshotFilter(f, &forgetOptions.SnapshotFilter, false) f.StringArrayVar(&forgetOptions.Hosts, "hostname", nil, "only consider snapshots with the given `hostname` (can be specified multiple times)") err := f.MarkDeprecated("hostname", "use --host") if err != nil { @@ -126,7 +126,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.Hosts, opts.Tags, opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, repo.Backend(), repo, &opts.SnapshotFilter, args) { snapshots = append(snapshots, sn) } diff --git a/cmd/restic/cmd_ls.go b/cmd/restic/cmd_ls.go index 7dd41ab21..aeaa750eb 100644 --- a/cmd/restic/cmd_ls.go +++ b/cmd/restic/cmd_ls.go @@ -49,7 +49,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er // LsOptions collects all options for the ls command. type LsOptions struct { ListLong bool - snapshotFilterOptions + restic.SnapshotFilter Recursive bool } @@ -59,7 +59,7 @@ func init() { cmdRoot.AddCommand(cmdLs) flags := cmdLs.Flags() - initSingleSnapshotFilterOptions(flags, &lsOptions.snapshotFilterOptions) + 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") } @@ -210,7 +210,11 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri } } - sn, err := restic.FindFilteredSnapshot(ctx, snapshotLister, repo, opts.Hosts, opts.Tags, opts.Paths, nil, args[0]) + sn, err := (&restic.SnapshotFilter{ + Hosts: opts.Hosts, + Paths: opts.Paths, + Tags: opts.Tags, + }).FindLatest(ctx, snapshotLister, repo, args[0]) if err != nil { return err } diff --git a/cmd/restic/cmd_mount.go b/cmd/restic/cmd_mount.go index 7afb30f7c..0501bfe89 100644 --- a/cmd/restic/cmd_mount.go +++ b/cmd/restic/cmd_mount.go @@ -77,7 +77,7 @@ type MountOptions struct { OwnerRoot bool AllowOther bool NoDefaultPermissions bool - snapshotFilterOptions + restic.SnapshotFilter TimeTemplate string PathTemplates []string } @@ -92,7 +92,7 @@ func init() { mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory") mountFlags.BoolVar(&mountOptions.NoDefaultPermissions, "no-default-permissions", false, "for 'allow-other', ignore Unix permissions and allow users to read all snapshot files") - initMultiSnapshotFilterOptions(mountFlags, &mountOptions.snapshotFilterOptions, true) + initMultiSnapshotFilter(mountFlags, &mountOptions.SnapshotFilter, true) mountFlags.StringArrayVar(&mountOptions.PathTemplates, "path-template", nil, "set `template` for path names (can be specified multiple times)") mountFlags.StringVar(&mountOptions.TimeTemplate, "snapshot-template", time.RFC3339, "set `template` to use for snapshot dirs") @@ -180,9 +180,7 @@ func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args cfg := fuse.Config{ OwnerIsRoot: opts.OwnerRoot, - Hosts: opts.Hosts, - Tags: opts.Tags, - Paths: opts.Paths, + Filter: opts.SnapshotFilter, TimeTemplate: opts.TimeTemplate, PathTemplates: opts.PathTemplates, } diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index b70cb52ff..579711662 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -42,7 +42,7 @@ type RestoreOptions struct { Include []string InsensitiveInclude []string Target string - snapshotFilterOptions + restic.SnapshotFilter Sparse bool Verify bool } @@ -59,7 +59,7 @@ func init() { flags.StringArrayVar(&restoreOptions.InsensitiveInclude, "iinclude", nil, "same as `--include` but ignores the casing of filenames") flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to") - initSingleSnapshotFilterOptions(flags, &restoreOptions.snapshotFilterOptions) + initSingleSnapshotFilter(flags, &restoreOptions.SnapshotFilter) flags.BoolVar(&restoreOptions.Sparse, "sparse", false, "restore files as sparse") flags.BoolVar(&restoreOptions.Verify, "verify", false, "verify restored files content") } @@ -131,7 +131,11 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, a } } - sn, err := restic.FindFilteredSnapshot(ctx, repo.Backend(), repo, opts.Hosts, opts.Tags, opts.Paths, nil, snapshotIDString) + sn, err := (&restic.SnapshotFilter{ + Hosts: opts.Hosts, + Paths: opts.Paths, + Tags: opts.Tags, + }).FindLatest(ctx, repo.Backend(), repo, snapshotIDString) if err != nil { return errors.Fatalf("failed to find snapshot: %v", err) } diff --git a/cmd/restic/cmd_rewrite.go b/cmd/restic/cmd_rewrite.go index cfe56db87..0d9aa1c8c 100644 --- a/cmd/restic/cmd_rewrite.go +++ b/cmd/restic/cmd_rewrite.go @@ -51,7 +51,7 @@ type RewriteOptions struct { Forget bool DryRun bool - snapshotFilterOptions + restic.SnapshotFilter excludePatternOptions } @@ -64,7 +64,7 @@ func init() { f.BoolVarP(&rewriteOptions.Forget, "forget", "", false, "remove original snapshots after creating new ones") f.BoolVarP(&rewriteOptions.DryRun, "dry-run", "n", false, "do not do anything, just print what would be done") - initMultiSnapshotFilterOptions(f, &rewriteOptions.snapshotFilterOptions, true) + initMultiSnapshotFilter(f, &rewriteOptions.SnapshotFilter, true) initExcludePatternOptions(f, &rewriteOptions.excludePatternOptions) } @@ -186,7 +186,7 @@ func runRewrite(ctx context.Context, opts RewriteOptions, gopts GlobalOptions, a } changedCount := 0 - for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, opts.Hosts, opts.Tags, opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, args) { Verbosef("\nsnapshot %s of %v at %s)\n", sn.ID().Str(), sn.Paths, sn.Time) changed, err := rewriteSnapshot(ctx, repo, sn, opts) if err != nil { diff --git a/cmd/restic/cmd_snapshots.go b/cmd/restic/cmd_snapshots.go index 0bfa4d110..c5faa044a 100644 --- a/cmd/restic/cmd_snapshots.go +++ b/cmd/restic/cmd_snapshots.go @@ -32,7 +32,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er // SnapshotOptions bundles all options for the snapshots command. type SnapshotOptions struct { - snapshotFilterOptions + restic.SnapshotFilter Compact bool Last bool // This option should be removed in favour of Latest. Latest int @@ -45,7 +45,7 @@ func init() { cmdRoot.AddCommand(cmdSnapshots) f := cmdSnapshots.Flags() - initMultiSnapshotFilterOptions(f, &snapshotOptions.snapshotFilterOptions, true) + initMultiSnapshotFilter(f, &snapshotOptions.SnapshotFilter, true) f.BoolVarP(&snapshotOptions.Compact, "compact", "c", false, "use compact output format") f.BoolVar(&snapshotOptions.Last, "last", false, "only show the last snapshot for each host and path") err := f.MarkDeprecated("last", "use --latest 1") @@ -73,7 +73,7 @@ func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions } var snapshots restic.Snapshots - for sn := range FindFilteredSnapshots(ctx, repo.Backend(), repo, opts.Hosts, opts.Tags, opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, repo.Backend(), repo, &opts.SnapshotFilter, args) { snapshots = append(snapshots, sn) } snapshotGroups, grouped, err := restic.GroupSnapshots(snapshots, opts.GroupBy) diff --git a/cmd/restic/cmd_stats.go b/cmd/restic/cmd_stats.go index 99d16b932..55ba6f254 100644 --- a/cmd/restic/cmd_stats.go +++ b/cmd/restic/cmd_stats.go @@ -58,7 +58,7 @@ type StatsOptions struct { // the mode of counting to perform (see consts for available modes) countMode string - snapshotFilterOptions + restic.SnapshotFilter } var statsOptions StatsOptions @@ -67,7 +67,7 @@ func init() { cmdRoot.AddCommand(cmdStats) f := cmdStats.Flags() f.StringVar(&statsOptions.countMode, "mode", countModeRestoreSize, "counting mode: restore-size (default), files-by-contents, blobs-per-file or raw-data") - initMultiSnapshotFilterOptions(f, &statsOptions.snapshotFilterOptions, true) + initMultiSnapshotFilter(f, &statsOptions.SnapshotFilter, true) } func runStats(ctx context.Context, gopts GlobalOptions, args []string) error { @@ -111,7 +111,7 @@ func runStats(ctx context.Context, gopts GlobalOptions, args []string) error { SnapshotsCount: 0, } - for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, statsOptions.Hosts, statsOptions.Tags, statsOptions.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &statsOptions.SnapshotFilter, args) { err = statsWalkSnapshot(ctx, sn, repo, stats) if err != nil { return fmt.Errorf("error walking snapshot: %v", err) diff --git a/cmd/restic/cmd_tag.go b/cmd/restic/cmd_tag.go index 222ddd04a..e5948ea02 100644 --- a/cmd/restic/cmd_tag.go +++ b/cmd/restic/cmd_tag.go @@ -35,7 +35,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er // TagOptions bundles all options for the 'tag' command. type TagOptions struct { - snapshotFilterOptions + restic.SnapshotFilter SetTags restic.TagLists AddTags restic.TagLists RemoveTags restic.TagLists @@ -50,7 +50,7 @@ func init() { tagFlags.Var(&tagOptions.SetTags, "set", "`tags` which will replace the existing tags in the format `tag[,tag,...]` (can be given multiple times)") tagFlags.Var(&tagOptions.AddTags, "add", "`tags` which will be added to the existing tags in the format `tag[,tag,...]` (can be given multiple times)") tagFlags.Var(&tagOptions.RemoveTags, "remove", "`tags` which will be removed from the existing tags in the format `tag[,tag,...]` (can be given multiple times)") - initMultiSnapshotFilterOptions(tagFlags, &tagOptions.snapshotFilterOptions, true) + initMultiSnapshotFilter(tagFlags, &tagOptions.SnapshotFilter, true) } func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string) (bool, error) { @@ -119,7 +119,7 @@ func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, args []st } changeCnt := 0 - for sn := range FindFilteredSnapshots(ctx, repo.Backend(), repo, opts.Hosts, opts.Tags, opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, repo.Backend(), repo, &opts.SnapshotFilter, args) { changed, err := changeTags(ctx, repo, sn, opts.SetTags.Flatten(), opts.AddTags.Flatten(), opts.RemoveTags.Flatten()) if err != nil { Warnf("unable to modify the tags for snapshot ID %q, ignoring: %v\n", sn.ID(), err) diff --git a/cmd/restic/find.go b/cmd/restic/find.go index 7b488c7aa..54d3563b1 100644 --- a/cmd/restic/find.go +++ b/cmd/restic/find.go @@ -8,34 +8,28 @@ import ( "github.com/spf13/pflag" ) -type snapshotFilterOptions struct { - Hosts []string - Tags restic.TagLists - Paths []string -} - -// initMultiSnapshotFilterOptions is used for commands that work on multiple snapshots +// initMultiSnapshotFilter is used for commands that work on multiple snapshots // MUST be combined with restic.FindFilteredSnapshots or FindFilteredSnapshots -func initMultiSnapshotFilterOptions(flags *pflag.FlagSet, options *snapshotFilterOptions, addHostShorthand bool) { +func initMultiSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter, addHostShorthand bool) { hostShorthand := "H" if !addHostShorthand { hostShorthand = "" } - flags.StringArrayVarP(&options.Hosts, "host", hostShorthand, nil, "only consider snapshots for this `host` (can be specified multiple times)") - flags.Var(&options.Tags, "tag", "only consider snapshots including `tag[,tag,...]` (can be specified multiple times)") - flags.StringArrayVar(&options.Paths, "path", nil, "only consider snapshots including this (absolute) `path` (can be specified multiple times)") + flags.StringArrayVarP(&filt.Hosts, "host", hostShorthand, nil, "only consider snapshots for this `host` (can be specified multiple times)") + flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]` (can be specified multiple times)") + flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path` (can be specified multiple times)") } -// initSingleSnapshotFilterOptions is used for commands that work on a single snapshot +// initSingleSnapshotFilter is used for commands that work on a single snapshot // MUST be combined with restic.FindFilteredSnapshot -func initSingleSnapshotFilterOptions(flags *pflag.FlagSet, options *snapshotFilterOptions) { - flags.StringArrayVarP(&options.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when snapshot ID \"latest\" is given (can be specified multiple times)") - flags.Var(&options.Tags, "tag", "only consider snapshots including `tag[,tag,...]`, when snapshot ID \"latest\" is given (can be specified multiple times)") - flags.StringArrayVar(&options.Paths, "path", nil, "only consider snapshots including this (absolute) `path`, when snapshot ID \"latest\" is given (can be specified multiple times)") +func initSingleSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter) { + flags.StringArrayVarP(&filt.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when snapshot ID \"latest\" is given (can be specified multiple times)") + flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]`, when snapshot ID \"latest\" is given (can be specified multiple times)") + flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path`, when snapshot ID \"latest\" is given (can be specified multiple times)") } // FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots. -func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, hosts []string, tags []restic.TagList, paths []string, snapshotIDs []string) <-chan *restic.Snapshot { +func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, f *restic.SnapshotFilter, snapshotIDs []string) <-chan *restic.Snapshot { out := make(chan *restic.Snapshot) go func() { defer close(out) @@ -45,7 +39,7 @@ func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic. return } - err = restic.FindFilteredSnapshots(ctx, be, loader, hosts, tags, paths, snapshotIDs, func(id string, sn *restic.Snapshot, err error) error { + err = f.FindAll(ctx, be, loader, snapshotIDs, func(id string, sn *restic.Snapshot, err error) error { if err != nil { Warnf("Ignoring %q: %v\n", id, err) } else { diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index 062a5954c..c87722f02 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -106,7 +106,7 @@ func testRunRestore(t testing.TB, opts GlobalOptions, dir string, snapshotID res func testRunRestoreLatest(t testing.TB, gopts GlobalOptions, dir string, paths []string, hosts []string) { opts := RestoreOptions{ Target: dir, - snapshotFilterOptions: snapshotFilterOptions{ + SnapshotFilter: restic.SnapshotFilter{ Hosts: hosts, Paths: paths, }, @@ -2196,7 +2196,7 @@ func TestFindListOnce(t *testing.T) { snapshotIDs := restic.NewIDSet() // specify the two oldest snapshots explicitly and use "latest" to reference the newest one - for sn := range FindFilteredSnapshots(context.TODO(), repo.Backend(), repo, nil, nil, nil, []string{ + for sn := range FindFilteredSnapshots(context.TODO(), repo.Backend(), repo, &restic.SnapshotFilter{}, []string{ secondSnapshot[0].String(), secondSnapshot[1].String()[:8], "latest", diff --git a/internal/fuse/root.go b/internal/fuse/root.go index fc8841964..ab6116f0d 100644 --- a/internal/fuse/root.go +++ b/internal/fuse/root.go @@ -16,9 +16,7 @@ import ( // Config holds settings for the fuse mount. type Config struct { OwnerIsRoot bool - Hosts []string - Tags []restic.TagList - Paths []string + Filter restic.SnapshotFilter TimeTemplate string PathTemplates []string } diff --git a/internal/fuse/snapshots_dirstruct.go b/internal/fuse/snapshots_dirstruct.go index f8e66d076..3080d4de8 100644 --- a/internal/fuse/snapshots_dirstruct.go +++ b/internal/fuse/snapshots_dirstruct.go @@ -295,7 +295,7 @@ func (d *SnapshotsDirStructure) updateSnapshots(ctx context.Context) error { } var snapshots restic.Snapshots - err := restic.FindFilteredSnapshots(ctx, d.root.repo.Backend(), d.root.repo, d.root.cfg.Hosts, d.root.cfg.Tags, d.root.cfg.Paths, nil, func(id string, sn *restic.Snapshot, err error) error { + err := d.root.cfg.Filter.FindAll(ctx, d.root.repo.Backend(), d.root.repo, nil, func(id string, sn *restic.Snapshot, err error) error { if sn != nil { snapshots = append(snapshots, sn) } diff --git a/internal/restic/snapshot_find.go b/internal/restic/snapshot_find.go index 4f8231a7f..4d4bb4957 100644 --- a/internal/restic/snapshot_find.go +++ b/internal/restic/snapshot_find.go @@ -12,13 +12,32 @@ import ( // ErrNoSnapshotFound is returned when no snapshot for the given criteria could be found. var ErrNoSnapshotFound = errors.New("no snapshot found") -// findLatestSnapshot finds latest snapshot with optional target/directory, tags, hostname, and timestamp filters. -func findLatestSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, hosts []string, - tags []TagList, paths []string, timeStampLimit *time.Time) (*Snapshot, error) { +// A SnapshotFilter denotes a set of snapshots based on hosts, tags and paths. +type SnapshotFilter struct { + _ struct{} // Force naming fields in literals. + + Hosts []string + Tags TagLists + Paths []string + // Match snapshots from before this timestamp. Zero for no limit. + TimestampLimit time.Time +} + +func (f *SnapshotFilter) empty() bool { + return len(f.Hosts)+len(f.Tags)+len(f.Paths) == 0 +} + +func (f *SnapshotFilter) matches(sn *Snapshot) bool { + return sn.HasHostname(f.Hosts) && sn.HasTagList(f.Tags) && sn.HasPaths(f.Paths) +} + +// findLatest finds the latest snapshot with optional target/directory, +// tags, hostname, and timestamp filters. +func (f *SnapshotFilter) findLatest(ctx context.Context, be Lister, loader LoaderUnpacked) (*Snapshot, error) { var err error - absTargets := make([]string, 0, len(paths)) - for _, target := range paths { + absTargets := make([]string, 0, len(f.Paths)) + for _, target := range f.Paths { if !filepath.IsAbs(target) { target, err = filepath.Abs(target) if err != nil { @@ -35,7 +54,7 @@ func findLatestSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, h return errors.Errorf("Error loading snapshot %v: %v", id.Str(), err) } - if timeStampLimit != nil && snapshot.Time.After(*timeStampLimit) { + if !f.TimestampLimit.IsZero() && snapshot.Time.After(f.TimestampLimit) { return nil } @@ -43,15 +62,7 @@ func findLatestSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, h return nil } - if !snapshot.HasHostname(hosts) { - return nil - } - - if !snapshot.HasTagList(tags) { - return nil - } - - if !snapshot.HasPaths(absTargets) { + if !f.matches(snapshot) { return nil } @@ -85,12 +96,14 @@ func FindSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, s strin return LoadSnapshot(ctx, loader, id) } -// FindFilteredSnapshot returns either the latests from a filtered list of all snapshots or a snapshot specified by `snapshotID`. -func FindFilteredSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, hosts []string, tags []TagList, paths []string, timeStampLimit *time.Time, snapshotID string) (*Snapshot, error) { +// FindLatest returns either the latest of a filtered list of all snapshots +// or a snapshot specified by `snapshotID`. +func (f *SnapshotFilter) FindLatest(ctx context.Context, be Lister, loader LoaderUnpacked, snapshotID string) (*Snapshot, error) { if snapshotID == "latest" { - sn, err := findLatestSnapshot(ctx, be, loader, hosts, tags, paths, timeStampLimit) + sn, err := f.findLatest(ctx, be, loader) if err == ErrNoSnapshotFound { - err = fmt.Errorf("snapshot filter (Paths:%v Tags:%v Hosts:%v): %w", paths, tags, hosts, err) + err = fmt.Errorf("snapshot filter (Paths:%v Tags:%v Hosts:%v): %w", + f.Paths, f.Tags, f.Hosts, err) } return sn, err } @@ -99,8 +112,8 @@ func FindFilteredSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, type SnapshotFindCb func(string, *Snapshot, error) error -// FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots. -func FindFilteredSnapshots(ctx context.Context, be Lister, loader LoaderUnpacked, hosts []string, tags []TagList, paths []string, snapshotIDs []string, fn SnapshotFindCb) error { +// FindAll yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots. +func (f *SnapshotFilter) FindAll(ctx context.Context, be Lister, loader LoaderUnpacked, snapshotIDs []string, fn SnapshotFindCb) error { if len(snapshotIDs) != 0 { var err error usedFilter := false @@ -116,9 +129,10 @@ func FindFilteredSnapshots(ctx context.Context, be Lister, loader LoaderUnpacked usedFilter = true - sn, err = findLatestSnapshot(ctx, be, loader, hosts, tags, paths, nil) + sn, err = f.findLatest(ctx, be, loader) if err == ErrNoSnapshotFound { - err = errors.Errorf("no snapshot matched given filter (Paths:%v Tags:%v Hosts:%v)", paths, tags, hosts) + err = errors.Errorf("no snapshot matched given filter (Paths:%v Tags:%v Hosts:%v)", + f.Paths, f.Tags, f.Hosts) } if sn != nil { ids.Insert(*sn.ID()) @@ -141,18 +155,14 @@ func FindFilteredSnapshots(ctx context.Context, be Lister, loader LoaderUnpacked } // Give the user some indication their filters are not used. - if !usedFilter && (len(hosts) != 0 || len(tags) != 0 || len(paths) != 0) { + if !usedFilter && !f.empty() { return fn("filters", nil, errors.Errorf("explicit snapshot ids are given")) } return nil } return ForAllSnapshots(ctx, be, loader, nil, func(id ID, sn *Snapshot, err error) error { - if err != nil { - return fn(id.String(), sn, err) - } - - if !sn.HasHostname(hosts) || !sn.HasTagList(tags) || !sn.HasPaths(paths) { + if err == nil && !f.matches(sn) { return nil } diff --git a/internal/restic/snapshot_find_test.go b/internal/restic/snapshot_find_test.go index 3c587dde1..d098b5224 100644 --- a/internal/restic/snapshot_find_test.go +++ b/internal/restic/snapshot_find_test.go @@ -14,13 +14,14 @@ func TestFindLatestSnapshot(t *testing.T) { restic.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1, 0) latestSnapshot := restic.TestCreateSnapshot(t, repo, parseTimeUTC("2019-09-09 09:09:09"), 1, 0) - sn, err := restic.FindFilteredSnapshot(context.TODO(), repo.Backend(), repo, []string{"foo"}, []restic.TagList{}, []string{}, nil, "latest") + f := restic.SnapshotFilter{Hosts: []string{"foo"}} + sn, err := f.FindLatest(context.TODO(), repo.Backend(), repo, "latest") if err != nil { - t.Fatalf("FindLatestSnapshot returned error: %v", err) + t.Fatalf("FindLatest returned error: %v", err) } if *sn.ID() != *latestSnapshot.ID() { - t.Errorf("FindLatestSnapshot returned wrong snapshot ID: %v", *sn.ID()) + t.Errorf("FindLatest returned wrong snapshot ID: %v", *sn.ID()) } } @@ -30,14 +31,15 @@ func TestFindLatestSnapshotWithMaxTimestamp(t *testing.T) { desiredSnapshot := restic.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1, 0) restic.TestCreateSnapshot(t, repo, parseTimeUTC("2019-09-09 09:09:09"), 1, 0) - maxTimestamp := parseTimeUTC("2018-08-08 08:08:08") - - sn, err := restic.FindFilteredSnapshot(context.TODO(), repo.Backend(), repo, []string{"foo"}, []restic.TagList{}, []string{}, &maxTimestamp, "latest") + sn, err := (&restic.SnapshotFilter{ + Hosts: []string{"foo"}, + TimestampLimit: parseTimeUTC("2018-08-08 08:08:08"), + }).FindLatest(context.TODO(), repo.Backend(), repo, "latest") if err != nil { - t.Fatalf("FindLatestSnapshot returned error: %v", err) + t.Fatalf("FindLatest returned error: %v", err) } if *sn.ID() != *desiredSnapshot.ID() { - t.Errorf("FindLatestSnapshot returned wrong snapshot ID: %v", *sn.ID()) + t.Errorf("FindLatest returned wrong snapshot ID: %v", *sn.ID()) } } From 593eb710b413fe190477d6fa296aef1fcb662525 Mon Sep 17 00:00:00 2001 From: Ian Muge Date: Tue, 21 Feb 2023 11:28:52 +0100 Subject: [PATCH 04/16] added changelog --- changelog/unreleased/pull-4219 | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/pull-4219 diff --git a/changelog/unreleased/pull-4219 b/changelog/unreleased/pull-4219 new file mode 100644 index 000000000..7d20c3607 --- /dev/null +++ b/changelog/unreleased/pull-4219 @@ -0,0 +1,5 @@ +Enhancement: Upgrade Minio to 7.0.49 + +Upgraded to allow use of the ap-southeast-4 region (Melbourne) + +https://github.com/restic/restic/pull/4219 From 4304e01ca21885f0fa5a31106075c6a11c27c5e3 Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Tue, 7 Mar 2023 22:12:08 +0100 Subject: [PATCH 05/16] fuse: Report fuse.Attr.Blocks correctly Fixes #4239. --- changelog/unreleased/issue-4239 | 11 +++++++++++ internal/fuse/file.go | 2 +- internal/fuse/fuse_test.go | 32 ++++++++++++++++++++++++++++++++ internal/fuse/link.go | 2 +- internal/fuse/snapshots_dir.go | 2 +- 5 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 changelog/unreleased/issue-4239 diff --git a/changelog/unreleased/issue-4239 b/changelog/unreleased/issue-4239 new file mode 100644 index 000000000..247f3d9ed --- /dev/null +++ b/changelog/unreleased/issue-4239 @@ -0,0 +1,11 @@ +Bugfix: Correct number of blocks reported in mount point + +Restic mount points incorrectly reported the number of 512-byte (POSIX +standard) blocks for files and links, due to a rounding bug. In particular, +empty files were reported as taking one block instead of zero. + +The rounding is now fixed: the number of blocks reported is the file size +(or link target size), divided by 512 and rounded up to a whole number. + +https://github.com/restic/restic/issues/4239 +https://github.com/restic/restic/pull/4240 diff --git a/internal/fuse/file.go b/internal/fuse/file.go index 28ff5d450..35bc2a73e 100644 --- a/internal/fuse/file.go +++ b/internal/fuse/file.go @@ -50,7 +50,7 @@ func (f *file) Attr(ctx context.Context, a *fuse.Attr) error { a.Inode = f.inode a.Mode = f.node.Mode a.Size = f.node.Size - a.Blocks = (f.node.Size / blockSize) + 1 + a.Blocks = (f.node.Size + blockSize - 1) / blockSize a.BlockSize = blockSize a.Nlink = uint32(f.node.Links) diff --git a/internal/fuse/fuse_test.go b/internal/fuse/fuse_test.go index e71bf6fee..863c7672d 100644 --- a/internal/fuse/fuse_test.go +++ b/internal/fuse/fuse_test.go @@ -8,6 +8,7 @@ import ( "context" "math/rand" "os" + "strings" "testing" "time" @@ -216,6 +217,37 @@ func testTopUIDGID(t *testing.T, cfg Config, repo restic.Repository, uid, gid ui rtest.Equals(t, uint32(0), attr.Gid) } +// Test reporting of fuse.Attr.Blocks in multiples of 512. +func TestBlocks(t *testing.T) { + root := &Root{} + + for _, c := range []struct { + size, blocks uint64 + }{ + {0, 0}, + {1, 1}, + {511, 1}, + {512, 1}, + {513, 2}, + {1024, 2}, + {1025, 3}, + {41253, 81}, + } { + target := strings.Repeat("x", int(c.size)) + + for _, n := range []fs.Node{ + &file{root: root, node: &restic.Node{Size: uint64(c.size)}}, + &link{root: root, node: &restic.Node{LinkTarget: target}}, + &snapshotLink{root: root, snapshot: &restic.Snapshot{}, target: target}, + } { + var a fuse.Attr + err := n.Attr(context.TODO(), &a) + rtest.OK(t, err) + rtest.Equals(t, c.blocks, a.Blocks) + } + } +} + func TestInodeFromNode(t *testing.T) { node := &restic.Node{Name: "foo.txt", Type: "chardev", Links: 2} ino1 := inodeFromNode(1, node) diff --git a/internal/fuse/link.go b/internal/fuse/link.go index f910aadc4..47ee666a3 100644 --- a/internal/fuse/link.go +++ b/internal/fuse/link.go @@ -42,7 +42,7 @@ func (l *link) Attr(ctx context.Context, a *fuse.Attr) error { a.Nlink = uint32(l.node.Links) a.Size = uint64(len(l.node.LinkTarget)) - a.Blocks = 1 + a.Size/blockSize + a.Blocks = (a.Size + blockSize - 1) / blockSize return nil } diff --git a/internal/fuse/snapshots_dir.go b/internal/fuse/snapshots_dir.go index 977d0ab17..c19155741 100644 --- a/internal/fuse/snapshots_dir.go +++ b/internal/fuse/snapshots_dir.go @@ -142,7 +142,7 @@ func (l *snapshotLink) Attr(ctx context.Context, a *fuse.Attr) error { a.Inode = l.inode a.Mode = os.ModeSymlink | 0777 a.Size = uint64(len(l.target)) - a.Blocks = 1 + a.Size/blockSize + a.Blocks = (a.Size + blockSize - 1) / blockSize a.Uid = l.root.uid a.Gid = l.root.gid a.Atime = l.snapshot.Time From 3b24c15c3db794fbd9a79ac8159f109134cc4434 Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Tue, 21 Mar 2023 17:33:18 +0100 Subject: [PATCH 06/16] fuse: Mix inode hashes in a non-symmetric way Since 0.15 (#4020), inodes are generated as hashes of names, xor'd with the parent inode. That means that the inode of a/b/b is h(a/b/b) = h(a) ^ h(b) ^ h(b) = h(a). I.e., the grandchild has the same inode as the grandparent. GNU find trips over this because it thinks it has encountered a loop in the filesystem, and fails to search a/b/b. This happens more generally when the same name occurs an even number of times. Fix this by multiplying the parent by a large prime, so the combining operation is not longer symmetric in its arguments. This is what the FNV hash does, which we used prior to 0.15. The hash is now h(a/b/b) = h(b) ^ p*(h(b) ^ p*h(a)) Note that we already ensure that h(x) is never zero. Collisions can still occur, but they should be much less likely to occur within a single path. Fixes #4253. --- changelog/unreleased/issue-4253 | 18 ++++++++++++++++++ internal/fuse/fuse_test.go | 11 +++++++++++ internal/fuse/inode.go | 6 ++++-- 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 changelog/unreleased/issue-4253 diff --git a/changelog/unreleased/issue-4253 b/changelog/unreleased/issue-4253 new file mode 100644 index 000000000..2471eab0b --- /dev/null +++ b/changelog/unreleased/issue-4253 @@ -0,0 +1,18 @@ +Bugfix: Mount command should no longer create spurious filesystem loops + +When a backup contains a directory that has the same name as its parent, +say, a/b/b, and the GNU find command were run on this backup in a restic +mount, find command would refuse to traverse the lowest "b" directory, +instead printing "File system loop detected". This is due to the way the +restic mount command generates inode numbers for directories in the mount +point. + +The rule for generating these inode numbers was changed in 0.15.0. It has +now been changed again to avoid this issue. A perfect rule does not exist, +but the probability of this behavior occurring is now extremely small. +When it does occur, the mount point is not broken, and scripts that traverse +the mount point should work as long as they don't rely on inode numbers for +detecting filesystem loops. + +https://github.com/restic/restic/issues/4253 +https://github.com/restic/restic/pull/4255 diff --git a/internal/fuse/fuse_test.go b/internal/fuse/fuse_test.go index 863c7672d..9ca1ec0c6 100644 --- a/internal/fuse/fuse_test.go +++ b/internal/fuse/fuse_test.go @@ -258,6 +258,17 @@ func TestInodeFromNode(t *testing.T) { ino1 = inodeFromNode(1, node) ino2 = inodeFromNode(2, node) rtest.Assert(t, ino1 != ino2, "same inode %d but different parent", ino1) + + // Regression test: in a path a/b/b, the grandchild should not get the + // same inode as the grandparent. + a := &restic.Node{Name: "a", Type: "dir", Links: 2} + ab := &restic.Node{Name: "b", Type: "dir", Links: 2} + abb := &restic.Node{Name: "b", Type: "dir", Links: 2} + inoA := inodeFromNode(1, a) + inoAb := inodeFromNode(inoA, ab) + inoAbb := inodeFromNode(inoAb, abb) + rtest.Assert(t, inoA != inoAb, "inode(a/b) = inode(a)") + rtest.Assert(t, inoA != inoAbb, "inode(a/b/b) = inode(a)") } var sink uint64 diff --git a/internal/fuse/inode.go b/internal/fuse/inode.go index de975b167..5e2ece4ac 100644 --- a/internal/fuse/inode.go +++ b/internal/fuse/inode.go @@ -10,9 +10,11 @@ import ( "github.com/restic/restic/internal/restic" ) +const prime = 11400714785074694791 // prime1 from xxhash. + // inodeFromName generates an inode number for a file in a meta dir. func inodeFromName(parent uint64, name string) uint64 { - inode := parent ^ xxhash.Sum64String(cleanupNodeName(name)) + inode := prime*parent ^ xxhash.Sum64String(cleanupNodeName(name)) // Inode 0 is invalid and 1 is the root. Remap those. if inode < 2 { @@ -33,7 +35,7 @@ func inodeFromNode(parent uint64, node *restic.Node) (inode uint64) { } else { // Else, use the name and the parent inode. // node.{DeviceID,Inode} may not even be reliable. - inode = parent ^ xxhash.Sum64String(cleanupNodeName(node.Name)) + inode = prime*parent ^ xxhash.Sum64String(cleanupNodeName(node.Name)) } // Inode 0 is invalid and 1 is the root. Remap those. From 48e065d9710f07234ffa1ff93d0065a895f58531 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 14 Apr 2023 20:56:46 +0200 Subject: [PATCH 07/16] Sync dependency upgrades from master github.com/Azure/azure-sdk-for-go/sdk/storage/azblob still uses v0.5.1 as upgrading it would increase the minimum Go version on Solaris to 1.20. --- changelog/unreleased/issue-4275 | 4 ++ go.mod | 44 ++++++++-------- go.sum | 92 ++++++++++++++++----------------- 3 files changed, 72 insertions(+), 68 deletions(-) create mode 100644 changelog/unreleased/issue-4275 diff --git a/changelog/unreleased/issue-4275 b/changelog/unreleased/issue-4275 new file mode 100644 index 000000000..3fbe093fb --- /dev/null +++ b/changelog/unreleased/issue-4275 @@ -0,0 +1,4 @@ +Security: Update golang.org/x/net to address CVE-2022-41723 + +https://github.com/restic/restic/issues/4275 +https://github.com/restic/restic/pull/4213 diff --git a/go.mod b/go.mod index a172c6992..d0e514220 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/restic/restic require ( - cloud.google.com/go/storage v1.29.0 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 + cloud.google.com/go/storage v1.30.1 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1 github.com/anacrolix/fuse v0.2.0 github.com/cenkalti/backoff/v4 v4.2.0 @@ -12,9 +12,9 @@ require ( github.com/google/go-cmp v0.5.9 github.com/hashicorp/golang-lru/v2 v2.0.1 github.com/juju/ratelimit v1.0.2 - github.com/klauspost/compress v1.15.15 + github.com/klauspost/compress v1.16.0 github.com/kurin/blazer v0.5.4-0.20230113224640-3887e1ec64b5 - github.com/minio/minio-go/v7 v7.0.47 + github.com/minio/minio-go/v7 v7.0.50 github.com/minio/sha256-simd v1.0.0 github.com/ncw/swift/v2 v2.0.1 github.com/pkg/errors v0.9.1 @@ -24,35 +24,35 @@ require ( github.com/restic/chunker v0.4.0 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 - golang.org/x/crypto v0.5.0 - golang.org/x/net v0.5.0 - golang.org/x/oauth2 v0.4.0 + golang.org/x/crypto v0.7.0 + golang.org/x/net v0.8.0 + golang.org/x/oauth2 v0.6.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.4.0 - golang.org/x/term v0.4.0 - golang.org/x/text v0.6.0 - google.golang.org/api v0.108.0 + golang.org/x/sys v0.6.0 + golang.org/x/term v0.6.0 + golang.org/x/text v0.8.0 + google.golang.org/api v0.116.0 ) require ( - cloud.google.com/go v0.108.0 // indirect - cloud.google.com/go/compute v1.15.1 // indirect + cloud.google.com/go v0.110.0 // indirect + cloud.google.com/go/compute v1.19.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.10.0 // indirect + cloud.google.com/go/iam v0.13.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/dnaeon/go-vcr v1.2.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/pprof v0.0.0-20230111200839-76d1ae5aea2b // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect + github.com/googleapis/gax-go/v2 v2.8.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.3 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/kr/fs v0.1.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -63,9 +63,9 @@ require ( go.opencensus.io v0.24.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/grpc v1.52.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 // indirect + google.golang.org/grpc v1.54.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 08069a411..ce664c15a 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.108.0 h1:xntQwnfn8oHGX0crLVinvHM+AhXvi3QHQIEcX/2hiWk= -cloud.google.com/go v0.108.0/go.mod h1:lNUfQqusBJp0bgAg6qrHgYFYbTB+dOiob1itwnlD33Q= -cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/iam v0.10.0 h1:fpP/gByFs6US1ma53v7VxhvbJpO2Aapng6wabJ99MuI= -cloud.google.com/go/iam v0.10.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 h1:rTnT/Jrcm+figWlYz4Ixzt0SJVR2cMC8lvZcimipiEY= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= @@ -39,8 +39,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elithrar/simple-scrypt v1.3.0 h1:KIlOlxdoQf9JWKl5lMAJ28SY2URB0XTRDn2TckyzAZg= @@ -70,8 +70,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -82,17 +82,17 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20230111200839-76d1ae5aea2b h1:8htHrh2bw9c7Idkb7YNac+ZpTqLMjRpI+FWu51ltaQc= github.com/google/pprof v0.0.0-20230111200839-76d1ae5aea2b/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= @@ -103,12 +103,12 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kurin/blazer v0.5.4-0.20230113224640-3887e1ec64b5 h1:OUlGa6AAolmjyPtILbMJ8vHayz5wd4wBUloheGcMhfA= @@ -116,8 +116,8 @@ github.com/kurin/blazer v0.5.4-0.20230113224640-3887e1ec64b5/go.mod h1:4FCXMUWo9 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.47 h1:sLiuCKGSIcn/MI6lREmTzX91DX/oRau4ia0j6e6eOSs= -github.com/minio/minio-go/v7 v7.0.47/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= +github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k= +github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -172,8 +172,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -189,11 +189,11 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -214,17 +214,17 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -237,8 +237,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.108.0 h1:WVBc/faN0DkKtR43Q/7+tPny9ZoLZdIiAyG5Q9vFClg= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.116.0 h1:09tOPVufPwfm5W4aA8EizGHJ7BcoRDsIareM2a15gO4= +google.golang.org/api v0.116.0/go.mod h1:9cD4/t6uvd9naoEJFA+M96d0IuB6BqFuyhpw68+mRGg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= @@ -246,15 +246,15 @@ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 h1:0BOZf6qNozI3pkN3fJLwNubheHJYHhMh91GRFOWWK08= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -266,8 +266,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= From 07a44a88f2ebc9456ef2a75d2aa2a4ff25ccdc5d Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 14 Apr 2023 21:53:55 +0200 Subject: [PATCH 08/16] Fix snapshot filtering for relative paths in the backup command The snapshot filtering internally converts relative paths to absolute ones to ensure that the parent snapshots selection works for backups of relative paths. --- internal/restic/snapshot_find.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/restic/snapshot_find.go b/internal/restic/snapshot_find.go index 4d4bb4957..8d6f8c4b1 100644 --- a/internal/restic/snapshot_find.go +++ b/internal/restic/snapshot_find.go @@ -46,6 +46,7 @@ func (f *SnapshotFilter) findLatest(ctx context.Context, be Lister, loader Loade } absTargets = append(absTargets, filepath.Clean(target)) } + f.Paths = absTargets var latest *Snapshot From f342db7666fd49acee80e1c5c30acff04aa0c82c Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Sat, 11 Feb 2023 14:51:58 +0100 Subject: [PATCH 09/16] ui/termstatus: Quote funny filenames Fixes #2260, #4191. --- changelog/unreleased/issue-2260 | 13 +++++++++++ internal/ui/backup/text.go | 2 ++ internal/ui/termstatus/status.go | 29 ++++++++++++++++++------ internal/ui/termstatus/status_test.go | 32 ++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 changelog/unreleased/issue-2260 diff --git a/changelog/unreleased/issue-2260 b/changelog/unreleased/issue-2260 new file mode 100644 index 000000000..96c79a035 --- /dev/null +++ b/changelog/unreleased/issue-2260 @@ -0,0 +1,13 @@ +Bugfix: Exotic filenames no longer break restic backup's status output + +Restic backup shows the names of files that it is working on. In previous +versions of restic, those names were printed without first sanitizing them, +so that filenames containing newlines or terminal control characters could +mess up restic backup's output or even change the state of a terminal. + +Filenames are now checked and quoted if they contain non-printable or +non-Unicode characters. + +https://github.com/restic/restic/issues/2260 +https://github.com/restic/restic/issues/4191 +https://github.com/restic/restic/pull/4192 diff --git a/internal/ui/backup/text.go b/internal/ui/backup/text.go index 0c5f897dd..acb2a8d3a 100644 --- a/internal/ui/backup/text.go +++ b/internal/ui/backup/text.go @@ -86,6 +86,8 @@ func (b *TextProgress) Error(item string, err error) error { // CompleteItem is the status callback function for the archiver when a // file/dir has been saved successfully. func (b *TextProgress) CompleteItem(messageType, item string, previous, current *restic.Node, s archiver.ItemStats, d time.Duration) { + item = termstatus.Quote(item) + switch messageType { case "dir new": b.VV("new %v, saved in %.3fs (%v added, %v stored, %v metadata)", diff --git a/internal/ui/termstatus/status.go b/internal/ui/termstatus/status.go index fdc7e14f6..a1b7a5fcc 100644 --- a/internal/ui/termstatus/status.go +++ b/internal/ui/termstatus/status.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "os" + "strconv" "strings" "unicode" @@ -325,6 +326,7 @@ func wideRune(r rune) bool { } // SetStatus updates the status lines. +// The lines should not contain newlines; this method adds them. func (t *Terminal) SetStatus(lines []string) { if len(lines) == 0 { return @@ -341,21 +343,34 @@ func (t *Terminal) SetStatus(lines []string) { } } - // make sure that all lines have a line break and are not too long + // Sanitize lines and truncate them if they're too long. for i, line := range lines { - line = strings.TrimRight(line, "\n") + line = Quote(line) if width > 0 { line = Truncate(line, width-2) } - lines[i] = line + "\n" + if i < len(lines)-1 { // Last line gets no line break. + lines[i] = line + "\n" + } } - // make sure the last line does not have a line break - last := len(lines) - 1 - lines[last] = strings.TrimRight(lines[last], "\n") - select { case t.status <- status{lines: lines}: case <-t.closed: } } + +// Quote lines with funny characters in them, meaning control chars, newlines, +// tabs, anything else non-printable and invalid UTF-8. +// +// This is intended to produce a string that does not mess up the terminal +// rather than produce an unambiguous quoted string. +func Quote(line string) string { + for _, r := range line { + // The replacement character usually means the input is not UTF-8. + if r == unicode.ReplacementChar || !unicode.IsPrint(r) { + return strconv.Quote(line) + } + } + return line +} diff --git a/internal/ui/termstatus/status_test.go b/internal/ui/termstatus/status_test.go index ce18f42e6..40a908deb 100644 --- a/internal/ui/termstatus/status_test.go +++ b/internal/ui/termstatus/status_test.go @@ -1,6 +1,36 @@ package termstatus -import "testing" +import ( + "strconv" + "testing" + + rtest "github.com/restic/restic/internal/test" +) + +func TestQuote(t *testing.T) { + for _, c := range []struct { + in string + needQuote bool + }{ + {"foo.bar/baz", false}, + {"föó_bàŕ-bãẑ", false}, + {" foo ", false}, + {"foo bar", false}, + {"foo\nbar", true}, + {"foo\rbar", true}, + {"foo\abar", true}, + {"\xff", true}, + {`c:\foo\bar`, false}, + // Issue #2260: terminal control characters. + {"\x1bm_red_is_beautiful", true}, + } { + if c.needQuote { + rtest.Equals(t, strconv.Quote(c.in), Quote(c.in)) + } else { + rtest.Equals(t, c.in, Quote(c.in)) + } + } +} func TestTruncate(t *testing.T) { var tests = []struct { From 1e6e9f9bd00362d572c5e3f8f99dc22b82c8a7a8 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 23 Apr 2023 12:40:29 +0200 Subject: [PATCH 10/16] tweak changelogs --- changelog/unreleased/issue-2260 | 4 ++-- changelog/unreleased/issue-4211 | 8 ++++---- changelog/unreleased/issue-4239 | 6 +++--- changelog/unreleased/issue-4253 | 13 ++++++------- changelog/unreleased/pull-4180 | 2 +- changelog/unreleased/pull-4219 | 2 +- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/changelog/unreleased/issue-2260 b/changelog/unreleased/issue-2260 index 96c79a035..795354c22 100644 --- a/changelog/unreleased/issue-2260 +++ b/changelog/unreleased/issue-2260 @@ -1,9 +1,9 @@ Bugfix: Exotic filenames no longer break restic backup's status output -Restic backup shows the names of files that it is working on. In previous +Restic `backup` shows the names of files that it is working on. In previous versions of restic, those names were printed without first sanitizing them, so that filenames containing newlines or terminal control characters could -mess up restic backup's output or even change the state of a terminal. +mangle the status output or even change the state of a terminal. Filenames are now checked and quoted if they contain non-printable or non-Unicode characters. diff --git a/changelog/unreleased/issue-4211 b/changelog/unreleased/issue-4211 index 45b7aee83..214418986 100644 --- a/changelog/unreleased/issue-4211 +++ b/changelog/unreleased/issue-4211 @@ -1,8 +1,8 @@ -Bugfix: Restic dump now interprets --host and --path correctly +Bugfix: Make `dump` interpret --host and --path correctly -Restic dump previously confused its --host= and --path= -options: it looked for snapshots with paths called from hosts -called . It now treats the options as intended. +A regression in restic 0.15.0 caused `dump` to confuse its `--host=` and +`--path=` options: it looked for snapshots with paths called `` +from hosts called ``. It now treats the options as intended. https://github.com/restic/restic/issues/4211 https://github.com/restic/restic/pull/4212 diff --git a/changelog/unreleased/issue-4239 b/changelog/unreleased/issue-4239 index 247f3d9ed..24d8bfe4e 100644 --- a/changelog/unreleased/issue-4239 +++ b/changelog/unreleased/issue-4239 @@ -1,8 +1,8 @@ Bugfix: Correct number of blocks reported in mount point -Restic mount points incorrectly reported the number of 512-byte (POSIX -standard) blocks for files and links, due to a rounding bug. In particular, -empty files were reported as taking one block instead of zero. +Restic mount points reported and incorrect number of 512-byte (POSIX standard) +blocks for files and links due to a rounding bug. In particular, empty files +were reported as taking one block instead of zero. The rounding is now fixed: the number of blocks reported is the file size (or link target size), divided by 512 and rounded up to a whole number. diff --git a/changelog/unreleased/issue-4253 b/changelog/unreleased/issue-4253 index 2471eab0b..1226e6e6d 100644 --- a/changelog/unreleased/issue-4253 +++ b/changelog/unreleased/issue-4253 @@ -1,11 +1,10 @@ -Bugfix: Mount command should no longer create spurious filesystem loops +Bugfix: `mount` should no longer create spurious filesystem loops -When a backup contains a directory that has the same name as its parent, -say, a/b/b, and the GNU find command were run on this backup in a restic -mount, find command would refuse to traverse the lowest "b" directory, -instead printing "File system loop detected". This is due to the way the -restic mount command generates inode numbers for directories in the mount -point. +When a backup contains a directory that has the same name as its parent, say +`a/b/b`, and the GNU `find` command were run on this backup in a restic mount, +`find` would refuse to traverse the lowest `b` directory, instead printing +`File system loop detected`. This is due to the way the restic mount command +generates inode numbers for directories in the mount point. The rule for generating these inode numbers was changed in 0.15.0. It has now been changed again to avoid this issue. A perfect rule does not exist, diff --git a/changelog/unreleased/pull-4180 b/changelog/unreleased/pull-4180 index ff43feb2b..054791e0f 100644 --- a/changelog/unreleased/pull-4180 +++ b/changelog/unreleased/pull-4180 @@ -1,5 +1,5 @@ Enhancement: Add release binaries for riscv64 architecture on Linux -We've added release binaries for riscv64 architecture on Linux. +We've added release binaries for the riscv64 architecture on Linux. https://github.com/restic/restic/pull/4180 diff --git a/changelog/unreleased/pull-4219 b/changelog/unreleased/pull-4219 index 7d20c3607..4f605bd1d 100644 --- a/changelog/unreleased/pull-4219 +++ b/changelog/unreleased/pull-4219 @@ -1,5 +1,5 @@ Enhancement: Upgrade Minio to 7.0.49 -Upgraded to allow use of the ap-southeast-4 region (Melbourne) +Upgraded to allow use of the ap-southeast-4 region (Melbourne). https://github.com/restic/restic/pull/4219 From 0bac935dac8058a7a3ea85055544116f28f00c24 Mon Sep 17 00:00:00 2001 From: "Leo R. Lundgren" Date: Sun, 23 Apr 2023 22:09:52 +0200 Subject: [PATCH 11/16] doc: Polish changelogs --- changelog/unreleased/issue-2260 | 9 ++++----- changelog/unreleased/issue-4211 | 2 +- changelog/unreleased/issue-4239 | 4 ++-- changelog/unreleased/issue-4253 | 7 ++++--- changelog/unreleased/issue-4275 | 2 +- changelog/unreleased/pull-4180 | 3 ++- changelog/unreleased/pull-4219 | 4 ++-- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/changelog/unreleased/issue-2260 b/changelog/unreleased/issue-2260 index 795354c22..da4fe8e99 100644 --- a/changelog/unreleased/issue-2260 +++ b/changelog/unreleased/issue-2260 @@ -1,9 +1,8 @@ -Bugfix: Exotic filenames no longer break restic backup's status output +Bugfix: Sanitize filenames printed by `backup` during processing -Restic `backup` shows the names of files that it is working on. In previous -versions of restic, those names were printed without first sanitizing them, -so that filenames containing newlines or terminal control characters could -mangle the status output or even change the state of a terminal. +The `backup` command would previously not sanitize the filenames it printed +during processing, potentially causing newlines or terminal control characters +to mangle the status output or even change the state of a terminal. Filenames are now checked and quoted if they contain non-printable or non-Unicode characters. diff --git a/changelog/unreleased/issue-4211 b/changelog/unreleased/issue-4211 index 214418986..0d499977c 100644 --- a/changelog/unreleased/issue-4211 +++ b/changelog/unreleased/issue-4211 @@ -1,4 +1,4 @@ -Bugfix: Make `dump` interpret --host and --path correctly +Bugfix: Make `dump` interpret `--host` and `--path` correctly A regression in restic 0.15.0 caused `dump` to confuse its `--host=` and `--path=` options: it looked for snapshots with paths called `` diff --git a/changelog/unreleased/issue-4239 b/changelog/unreleased/issue-4239 index 24d8bfe4e..43d099e24 100644 --- a/changelog/unreleased/issue-4239 +++ b/changelog/unreleased/issue-4239 @@ -1,11 +1,11 @@ Bugfix: Correct number of blocks reported in mount point -Restic mount points reported and incorrect number of 512-byte (POSIX standard) +Restic mount points reported an incorrect number of 512-byte (POSIX standard) blocks for files and links due to a rounding bug. In particular, empty files were reported as taking one block instead of zero. The rounding is now fixed: the number of blocks reported is the file size -(or link target size), divided by 512 and rounded up to a whole number. +(or link target size) divided by 512 and rounded up to a whole number. https://github.com/restic/restic/issues/4239 https://github.com/restic/restic/pull/4240 diff --git a/changelog/unreleased/issue-4253 b/changelog/unreleased/issue-4253 index 1226e6e6d..d9109f988 100644 --- a/changelog/unreleased/issue-4253 +++ b/changelog/unreleased/issue-4253 @@ -1,14 +1,15 @@ -Bugfix: `mount` should no longer create spurious filesystem loops +Bugfix: Minimize risk of spurious filesystem loops with `mount` When a backup contains a directory that has the same name as its parent, say -`a/b/b`, and the GNU `find` command were run on this backup in a restic mount, +`a/b/b`, and the GNU `find` command was run on this backup in a restic mount, `find` would refuse to traverse the lowest `b` directory, instead printing -`File system loop detected`. This is due to the way the restic mount command +`File system loop detected`. This was due to the way the restic mount command generates inode numbers for directories in the mount point. The rule for generating these inode numbers was changed in 0.15.0. It has now been changed again to avoid this issue. A perfect rule does not exist, but the probability of this behavior occurring is now extremely small. + When it does occur, the mount point is not broken, and scripts that traverse the mount point should work as long as they don't rely on inode numbers for detecting filesystem loops. diff --git a/changelog/unreleased/issue-4275 b/changelog/unreleased/issue-4275 index 3fbe093fb..944797b85 100644 --- a/changelog/unreleased/issue-4275 +++ b/changelog/unreleased/issue-4275 @@ -1,4 +1,4 @@ -Security: Update golang.org/x/net to address CVE-2022-41723 +Security: Update golang.org/x/net to address CVE-2022-41723 https://github.com/restic/restic/issues/4275 https://github.com/restic/restic/pull/4213 diff --git a/changelog/unreleased/pull-4180 b/changelog/unreleased/pull-4180 index 054791e0f..511974963 100644 --- a/changelog/unreleased/pull-4180 +++ b/changelog/unreleased/pull-4180 @@ -1,5 +1,6 @@ Enhancement: Add release binaries for riscv64 architecture on Linux -We've added release binaries for the riscv64 architecture on Linux. +Builds for the `riscv64` architecture on Linux are now included in the +release binaries. https://github.com/restic/restic/pull/4180 diff --git a/changelog/unreleased/pull-4219 b/changelog/unreleased/pull-4219 index 4f605bd1d..25da7058b 100644 --- a/changelog/unreleased/pull-4219 +++ b/changelog/unreleased/pull-4219 @@ -1,5 +1,5 @@ -Enhancement: Upgrade Minio to 7.0.49 +Enhancement: Upgrade Minio to version 7.0.49 -Upgraded to allow use of the ap-southeast-4 region (Melbourne). +The upgraded version now allows use of the `ap-southeast-4` region (Melbourne). https://github.com/restic/restic/pull/4219 From 0aaa4e6cbecd2b46f9be0ad8c70fc9b2f3419677 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 24 Apr 2023 20:28:24 +0200 Subject: [PATCH 12/16] Prepare changelog for 0.15.2 --- changelog/{unreleased => 0.15.2_2023-04-24}/issue-2260 | 0 changelog/{unreleased => 0.15.2_2023-04-24}/issue-4211 | 0 changelog/{unreleased => 0.15.2_2023-04-24}/issue-4239 | 0 changelog/{unreleased => 0.15.2_2023-04-24}/issue-4253 | 0 changelog/{unreleased => 0.15.2_2023-04-24}/issue-4275 | 0 changelog/{unreleased => 0.15.2_2023-04-24}/pull-4180 | 0 changelog/{unreleased => 0.15.2_2023-04-24}/pull-4219 | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename changelog/{unreleased => 0.15.2_2023-04-24}/issue-2260 (100%) rename changelog/{unreleased => 0.15.2_2023-04-24}/issue-4211 (100%) rename changelog/{unreleased => 0.15.2_2023-04-24}/issue-4239 (100%) rename changelog/{unreleased => 0.15.2_2023-04-24}/issue-4253 (100%) rename changelog/{unreleased => 0.15.2_2023-04-24}/issue-4275 (100%) rename changelog/{unreleased => 0.15.2_2023-04-24}/pull-4180 (100%) rename changelog/{unreleased => 0.15.2_2023-04-24}/pull-4219 (100%) diff --git a/changelog/unreleased/issue-2260 b/changelog/0.15.2_2023-04-24/issue-2260 similarity index 100% rename from changelog/unreleased/issue-2260 rename to changelog/0.15.2_2023-04-24/issue-2260 diff --git a/changelog/unreleased/issue-4211 b/changelog/0.15.2_2023-04-24/issue-4211 similarity index 100% rename from changelog/unreleased/issue-4211 rename to changelog/0.15.2_2023-04-24/issue-4211 diff --git a/changelog/unreleased/issue-4239 b/changelog/0.15.2_2023-04-24/issue-4239 similarity index 100% rename from changelog/unreleased/issue-4239 rename to changelog/0.15.2_2023-04-24/issue-4239 diff --git a/changelog/unreleased/issue-4253 b/changelog/0.15.2_2023-04-24/issue-4253 similarity index 100% rename from changelog/unreleased/issue-4253 rename to changelog/0.15.2_2023-04-24/issue-4253 diff --git a/changelog/unreleased/issue-4275 b/changelog/0.15.2_2023-04-24/issue-4275 similarity index 100% rename from changelog/unreleased/issue-4275 rename to changelog/0.15.2_2023-04-24/issue-4275 diff --git a/changelog/unreleased/pull-4180 b/changelog/0.15.2_2023-04-24/pull-4180 similarity index 100% rename from changelog/unreleased/pull-4180 rename to changelog/0.15.2_2023-04-24/pull-4180 diff --git a/changelog/unreleased/pull-4219 b/changelog/0.15.2_2023-04-24/pull-4219 similarity index 100% rename from changelog/unreleased/pull-4219 rename to changelog/0.15.2_2023-04-24/pull-4219 From 1f3f042f32caff8a4e74c5da3545335133d5f8c1 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 24 Apr 2023 20:28:24 +0200 Subject: [PATCH 13/16] Generate CHANGELOG.md for 0.15.2 --- CHANGELOG.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5635404..a502d49e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,92 @@ +Changelog for restic 0.15.2 (2023-04-24) +======================================= + +The following sections list the changes in restic 0.15.2 relevant to +restic users. The changes are ordered by importance. + +Summary +------- + + * Sec #4275: Update golang.org/x/net to address CVE-2022-41723 + * Fix #2260: Sanitize filenames printed by `backup` during processing + * Fix #4211: Make `dump` interpret `--host` and `--path` correctly + * Fix #4239: Correct number of blocks reported in mount point + * Fix #4253: Minimize risk of spurious filesystem loops with `mount` + * Enh #4180: Add release binaries for riscv64 architecture on Linux + * Enh #4219: Upgrade Minio to version 7.0.49 + +Details +------- + + * Security #4275: Update golang.org/x/net to address CVE-2022-41723 + + https://github.com/restic/restic/issues/4275 + https://github.com/restic/restic/pull/4213 + + * Bugfix #2260: Sanitize filenames printed by `backup` during processing + + The `backup` command would previously not sanitize the filenames it printed during + processing, potentially causing newlines or terminal control characters to mangle the + status output or even change the state of a terminal. + + Filenames are now checked and quoted if they contain non-printable or non-Unicode + characters. + + https://github.com/restic/restic/issues/2260 + https://github.com/restic/restic/issues/4191 + https://github.com/restic/restic/pull/4192 + + * Bugfix #4211: Make `dump` interpret `--host` and `--path` correctly + + A regression in restic 0.15.0 caused `dump` to confuse its `--host=` and + `--path=` options: it looked for snapshots with paths called `` from hosts + called ``. It now treats the options as intended. + + https://github.com/restic/restic/issues/4211 + https://github.com/restic/restic/pull/4212 + + * Bugfix #4239: Correct number of blocks reported in mount point + + Restic mount points reported an incorrect number of 512-byte (POSIX standard) blocks for + files and links due to a rounding bug. In particular, empty files were reported as taking one + block instead of zero. + + The rounding is now fixed: the number of blocks reported is the file size (or link target size) + divided by 512 and rounded up to a whole number. + + https://github.com/restic/restic/issues/4239 + https://github.com/restic/restic/pull/4240 + + * Bugfix #4253: Minimize risk of spurious filesystem loops with `mount` + + When a backup contains a directory that has the same name as its parent, say `a/b/b`, and the GNU + `find` command was run on this backup in a restic mount, `find` would refuse to traverse the + lowest `b` directory, instead printing `File system loop detected`. This was due to the way the + restic mount command generates inode numbers for directories in the mount point. + + The rule for generating these inode numbers was changed in 0.15.0. It has now been changed again + to avoid this issue. A perfect rule does not exist, but the probability of this behavior + occurring is now extremely small. + + When it does occur, the mount point is not broken, and scripts that traverse the mount point + should work as long as they don't rely on inode numbers for detecting filesystem loops. + + https://github.com/restic/restic/issues/4253 + https://github.com/restic/restic/pull/4255 + + * Enhancement #4180: Add release binaries for riscv64 architecture on Linux + + Builds for the `riscv64` architecture on Linux are now included in the release binaries. + + https://github.com/restic/restic/pull/4180 + + * Enhancement #4219: Upgrade Minio to version 7.0.49 + + The upgraded version now allows use of the `ap-southeast-4` region (Melbourne). + + https://github.com/restic/restic/pull/4219 + + Changelog for restic 0.15.1 (2023-01-30) ======================================= From db6b4f8912519e908a9b53609019ec7a075f99dd Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 24 Apr 2023 20:28:37 +0200 Subject: [PATCH 14/16] Update manpages and auto-completion --- doc/man/restic-backup.1 | 2 +- doc/man/restic-cache.1 | 2 +- doc/man/restic-cat.1 | 2 +- doc/man/restic-check.1 | 2 +- doc/man/restic-copy.1 | 2 +- doc/man/restic-diff.1 | 2 +- doc/man/restic-dump.1 | 2 +- doc/man/restic-find.1 | 2 +- doc/man/restic-forget.1 | 2 +- doc/man/restic-generate.1 | 2 +- doc/man/restic-init.1 | 2 +- doc/man/restic-key.1 | 2 +- doc/man/restic-list.1 | 2 +- doc/man/restic-ls.1 | 2 +- doc/man/restic-migrate.1 | 2 +- doc/man/restic-mount.1 | 2 +- doc/man/restic-prune.1 | 2 +- doc/man/restic-rebuild-index.1 | 2 +- doc/man/restic-recover.1 | 2 +- doc/man/restic-restore.1 | 2 +- doc/man/restic-rewrite.1 | 2 +- doc/man/restic-self-update.1 | 2 +- doc/man/restic-snapshots.1 | 2 +- doc/man/restic-stats.1 | 2 +- doc/man/restic-tag.1 | 2 +- doc/man/restic-unlock.1 | 2 +- doc/man/restic-version.1 | 2 +- doc/man/restic.1 | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/doc/man/restic-backup.1 b/doc/man/restic-backup.1 index 2598678d0..4297c3b8e 100644 --- a/doc/man/restic-backup.1 +++ b/doc/man/restic-backup.1 @@ -205,7 +205,7 @@ Exit status is 3 if some source data could not be read (incomplete snapshot crea .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-cache.1 b/doc/man/restic-cache.1 index 302bcb01e..3552fb1dc 100644 --- a/doc/man/restic-cache.1 +++ b/doc/man/restic-cache.1 @@ -118,7 +118,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-cat.1 b/doc/man/restic-cat.1 index 1fb7dd45f..2e787fa06 100644 --- a/doc/man/restic-cat.1 +++ b/doc/man/restic-cat.1 @@ -106,7 +106,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-check.1 b/doc/man/restic-check.1 index e08b83d46..e641fc2b5 100644 --- a/doc/man/restic-check.1 +++ b/doc/man/restic-check.1 @@ -123,7 +123,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-copy.1 b/doc/man/restic-copy.1 index 07dcfe957..53badecc9 100644 --- a/doc/man/restic-copy.1 +++ b/doc/man/restic-copy.1 @@ -147,7 +147,7 @@ new destination repository using the "init" command. .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-diff.1 b/doc/man/restic-diff.1 index f0707a257..31c34dc8a 100644 --- a/doc/man/restic-diff.1 +++ b/doc/man/restic-diff.1 @@ -126,7 +126,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-dump.1 b/doc/man/restic-dump.1 index f9a2368bc..61b3b3ec8 100644 --- a/doc/man/restic-dump.1 +++ b/doc/man/restic-dump.1 @@ -129,7 +129,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-find.1 b/doc/man/restic-find.1 index 4f5bdd4e3..9fa4dd811 100644 --- a/doc/man/restic-find.1 +++ b/doc/man/restic-find.1 @@ -151,7 +151,7 @@ It can also be used to search for restic blobs or trees for troubleshooting. .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH EXAMPLE diff --git a/doc/man/restic-forget.1 b/doc/man/restic-forget.1 index f46d05736..d8a69856e 100644 --- a/doc/man/restic-forget.1 +++ b/doc/man/restic-forget.1 @@ -217,7 +217,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-generate.1 b/doc/man/restic-generate.1 index e3733ce60..6b54ebfca 100644 --- a/doc/man/restic-generate.1 +++ b/doc/man/restic-generate.1 @@ -127,7 +127,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-init.1 b/doc/man/restic-init.1 index 80edf5362..194f31756 100644 --- a/doc/man/restic-init.1 +++ b/doc/man/restic-init.1 @@ -134,7 +134,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-key.1 b/doc/man/restic-key.1 index ff6ab4fd0..4163cefa5 100644 --- a/doc/man/restic-key.1 +++ b/doc/man/restic-key.1 @@ -118,7 +118,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-list.1 b/doc/man/restic-list.1 index e2f878c76..6683e2c47 100644 --- a/doc/man/restic-list.1 +++ b/doc/man/restic-list.1 @@ -106,7 +106,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-ls.1 b/doc/man/restic-ls.1 index afd72ff71..a16716434 100644 --- a/doc/man/restic-ls.1 +++ b/doc/man/restic-ls.1 @@ -141,7 +141,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-migrate.1 b/doc/man/restic-migrate.1 index ee4d44e71..d8127090e 100644 --- a/doc/man/restic-migrate.1 +++ b/doc/man/restic-migrate.1 @@ -112,7 +112,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-mount.1 b/doc/man/restic-mount.1 index da38ae451..ce4f893a7 100644 --- a/doc/man/restic-mount.1 +++ b/doc/man/restic-mount.1 @@ -190,7 +190,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-prune.1 b/doc/man/restic-prune.1 index 88c03f72a..197cb1130 100644 --- a/doc/man/restic-prune.1 +++ b/doc/man/restic-prune.1 @@ -135,7 +135,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-rebuild-index.1 b/doc/man/restic-rebuild-index.1 index 3be67e79e..18878b66f 100644 --- a/doc/man/restic-rebuild-index.1 +++ b/doc/man/restic-rebuild-index.1 @@ -111,7 +111,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-recover.1 b/doc/man/restic-recover.1 index 7415a1113..aa3441156 100644 --- a/doc/man/restic-recover.1 +++ b/doc/man/restic-recover.1 @@ -108,7 +108,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-restore.1 b/doc/man/restic-restore.1 index 2348f7478..39ff62059 100644 --- a/doc/man/restic-restore.1 +++ b/doc/man/restic-restore.1 @@ -151,7 +151,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-rewrite.1 b/doc/man/restic-rewrite.1 index 9f33bcb64..6edf51b95 100644 --- a/doc/man/restic-rewrite.1 +++ b/doc/man/restic-rewrite.1 @@ -159,7 +159,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-self-update.1 b/doc/man/restic-self-update.1 index 25f863396..e311b2277 100644 --- a/doc/man/restic-self-update.1 +++ b/doc/man/restic-self-update.1 @@ -113,7 +113,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-snapshots.1 b/doc/man/restic-snapshots.1 index 78cd664e3..d2dbf52ee 100644 --- a/doc/man/restic-snapshots.1 +++ b/doc/man/restic-snapshots.1 @@ -130,7 +130,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-stats.1 b/doc/man/restic-stats.1 index 6e3b9838b..694bde22d 100644 --- a/doc/man/restic-stats.1 +++ b/doc/man/restic-stats.1 @@ -152,7 +152,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-tag.1 b/doc/man/restic-tag.1 index 06bf25495..1ff0b4f78 100644 --- a/doc/man/restic-tag.1 +++ b/doc/man/restic-tag.1 @@ -137,7 +137,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-unlock.1 b/doc/man/restic-unlock.1 index c4ad7f050..e5b408915 100644 --- a/doc/man/restic-unlock.1 +++ b/doc/man/restic-unlock.1 @@ -110,7 +110,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic-version.1 b/doc/man/restic-version.1 index b410d1231..eca34d60a 100644 --- a/doc/man/restic-version.1 +++ b/doc/man/restic-version.1 @@ -107,7 +107,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO diff --git a/doc/man/restic.1 b/doc/man/restic.1 index 76602d02d..f76d16e38 100644 --- a/doc/man/restic.1 +++ b/doc/man/restic.1 @@ -100,7 +100,7 @@ directories in an encrypted repository stored on different backends. .PP \fB-v\fP, \fB--verbose\fP[=0] - be verbose (specify multiple times or a level using --verbose=\fB\fCn\fR, max level/times is 2) + be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2) .SH SEE ALSO From be8be3397c2ba4f3ec986998a5af4a5a4c309bc3 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 24 Apr 2023 20:28:37 +0200 Subject: [PATCH 15/16] Add version for 0.15.2 --- VERSION | 2 +- cmd/restic/global.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index e815b861f..4312e0d0c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.15.1 +0.15.2 diff --git a/cmd/restic/global.go b/cmd/restic/global.go index b32265275..206229d94 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -42,7 +42,7 @@ import ( "golang.org/x/term" ) -var version = "0.15.1" +var version = "0.15.2" // TimeFormat is the format used for all timestamps printed by restic. const TimeFormat = "2006-01-02 15:04:05" From ac7ac0cb9725e5abc7f7fe4191672337451f6ca9 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 24 Apr 2023 20:28:37 +0200 Subject: [PATCH 16/16] Set development version for 0.15.2 --- cmd/restic/global.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/restic/global.go b/cmd/restic/global.go index 206229d94..d8b8b6a96 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -42,7 +42,7 @@ import ( "golang.org/x/term" ) -var version = "0.15.2" +var version = "0.15.2-dev (compiled manually)" // TimeFormat is the format used for all timestamps printed by restic. const TimeFormat = "2006-01-02 15:04:05"