forked from TrueCloudLab/restic
backup: support specifying volume instead of path on Windows
"C:" (volume name) versus "C:\" (path)
This commit is contained in:
parent
a0f2dfbc19
commit
4fcedb4bae
3 changed files with 85 additions and 1 deletions
19
changelog/unreleased/issue-2004
Normal file
19
changelog/unreleased/issue-2004
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Bugfix: Correctly handle passing volume name to `backup` command
|
||||||
|
|
||||||
|
On Windows, when the specified backup target only included the volume
|
||||||
|
name without a trailing slash, for example, `C:`, then restoring the
|
||||||
|
resulting snapshot would result in an error. Note that using `C:\`
|
||||||
|
as backup target worked correctly.
|
||||||
|
|
||||||
|
Specifying volume names now works correctly.
|
||||||
|
|
||||||
|
To restore snapshots created before this bugfix, use the `<snapshot>:<subpath>`
|
||||||
|
syntax. For a snapshot with ID `12345678` and a backup of `C:`, the following
|
||||||
|
command can be used:
|
||||||
|
|
||||||
|
```
|
||||||
|
restic restore 12345678:/C/C:./ --target output/folder
|
||||||
|
```
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/2004
|
||||||
|
https://github.com/restic/restic/pull/5028
|
|
@ -715,7 +715,12 @@ func resolveRelativeTargets(filesys fs.FS, targets []string) ([]string, error) {
|
||||||
debug.Log("targets before resolving: %v", targets)
|
debug.Log("targets before resolving: %v", targets)
|
||||||
result := make([]string, 0, len(targets))
|
result := make([]string, 0, len(targets))
|
||||||
for _, target := range targets {
|
for _, target := range targets {
|
||||||
target = filesys.Clean(target)
|
if target != "" && filesys.VolumeName(target) == target {
|
||||||
|
// special case to allow users to also specify a volume name "C:" instead of a path "C:\"
|
||||||
|
target = target + filesys.Separator()
|
||||||
|
} else {
|
||||||
|
target = filesys.Clean(target)
|
||||||
|
}
|
||||||
pc, _ := pathComponents(filesys, target, false)
|
pc, _ := pathComponents(filesys, target, false)
|
||||||
if len(pc) > 0 {
|
if len(pc) > 0 {
|
||||||
result = append(result, target)
|
result = append(result, target)
|
||||||
|
|
|
@ -1448,6 +1448,66 @@ func TestArchiverSnapshot(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveRelativeTargetsSpecial(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
targets []string
|
||||||
|
expected []string
|
||||||
|
win bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic relative path",
|
||||||
|
targets: []string{filepath.FromSlash("some/path")},
|
||||||
|
expected: []string{filepath.FromSlash("some/path")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial relative path",
|
||||||
|
targets: []string{filepath.FromSlash("../some/path")},
|
||||||
|
expected: []string{filepath.FromSlash("../some/path")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic absolute path",
|
||||||
|
targets: []string{filepath.FromSlash("/some/path")},
|
||||||
|
expected: []string{filepath.FromSlash("/some/path")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "volume name",
|
||||||
|
targets: []string{"C:"},
|
||||||
|
expected: []string{"C:\\"},
|
||||||
|
win: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "volume root path",
|
||||||
|
targets: []string{"C:\\"},
|
||||||
|
expected: []string{"C:\\"},
|
||||||
|
win: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UNC path",
|
||||||
|
targets: []string{"\\\\server\\volume"},
|
||||||
|
expected: []string{"\\\\server\\volume\\"},
|
||||||
|
win: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UNC path with trailing slash",
|
||||||
|
targets: []string{"\\\\server\\volume\\"},
|
||||||
|
expected: []string{"\\\\server\\volume\\"},
|
||||||
|
win: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if test.win && runtime.GOOS != "windows" {
|
||||||
|
t.Skip("skip test on unix")
|
||||||
|
}
|
||||||
|
|
||||||
|
targets, err := resolveRelativeTargets(&fs.Local{}, test.targets)
|
||||||
|
rtest.OK(t, err)
|
||||||
|
rtest.Equals(t, test.expected, targets)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestArchiverSnapshotSelect(t *testing.T) {
|
func TestArchiverSnapshotSelect(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
name string
|
name string
|
||||||
|
|
Loading…
Reference in a new issue