From af50fe9ac0c3f0222ac4dc9169a72f3a215ab6c6 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 19 Aug 2022 18:17:57 +0200 Subject: [PATCH] mount: Map slashes in tags to underscores Suggested-by: greatroar <> --- changelog/unreleased/issue-2907 | 5 +++++ internal/fuse/snapshots_dirstruct.go | 19 +++++++++++++++++++ internal/fuse/snapshots_dirstruct_test.go | 16 ++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/changelog/unreleased/issue-2907 b/changelog/unreleased/issue-2907 index ca21d0ef6..6cb67553f 100644 --- a/changelog/unreleased/issue-2907 +++ b/changelog/unreleased/issue-2907 @@ -6,5 +6,10 @@ formatting of the time for a snapshot is now controlled using `--time-template` and supports subdirectories to for example group snapshots by year. Please refer to the help output of the `mount` command for further details. +Characters in tag names which are not allowed in a filename are replaced by +underscores `_`. For example a tag `foo/bar` will result in a directory name +of `foo_bar`. + https://github.com/restic/restic/issues/2907 https://github.com/restic/restic/pull/2913 +https://github.com/restic/restic/pull/3691 diff --git a/internal/fuse/snapshots_dirstruct.go b/internal/fuse/snapshots_dirstruct.go index 18831dcb1..2d1f64fd4 100644 --- a/internal/fuse/snapshots_dirstruct.go +++ b/internal/fuse/snapshots_dirstruct.go @@ -97,6 +97,7 @@ func pathsFromSn(pathTemplate string, timeTemplate string, sn *restic.Snapshot) // needs special treatment: Rebuild the string builders newout := make([]strings.Builder, len(out)*len(sn.Tags)) for i, tag := range sn.Tags { + tag = filenameFromTag(tag) for j := range out { newout[i*len(out)+j].WriteString(out[j].String() + tag) } @@ -139,6 +140,24 @@ func pathsFromSn(pathTemplate string, timeTemplate string, sn *restic.Snapshot) return paths, timeSuffix } +// Some tags are problematic when used as filenames: +// +// "" +// ".", ".." +// anything containing '/' +// +// Replace all special character by underscores "_", an empty tag is also represented as a underscore. +func filenameFromTag(tag string) string { + switch tag { + case "", ".": + return "_" + case "..": + return "__" + } + + return strings.ReplaceAll(tag, "/", "_") +} + // determine static path prefix func staticPrefix(pathTemplate string) (prefix string) { inVerb := false diff --git a/internal/fuse/snapshots_dirstruct_test.go b/internal/fuse/snapshots_dirstruct_test.go index 3a6d397be..1e823a475 100644 --- a/internal/fuse/snapshots_dirstruct_test.go +++ b/internal/fuse/snapshots_dirstruct_test.go @@ -273,3 +273,19 @@ func TestMakeEmptyDirs(t *testing.T) { verifyEntries(t, expNames, expLatest, sds.entries) } + +func TestFilenameFromTag(t *testing.T) { + for _, c := range []struct { + tag, filename string + }{ + {"", "_"}, + {".", "_"}, + {"..", "__"}, + {"%.", "%."}, + {"foo", "foo"}, + {"foo ", "foo "}, + {"foo/bar_baz", "foo_bar_baz"}, + } { + test.Equals(t, c.filename, filenameFromTag(c.tag)) + } +}