From 8b321387c082c178a85c8bd1b4047ff95e7a63e3 Mon Sep 17 00:00:00 2001 From: nf Date: Fri, 12 Jan 2024 20:46:18 +1100 Subject: [PATCH] lib/versioner: Expand tildes in version directory (fixes #9241) (#9327) ### Purpose Fix #9241 by expanding tildes in version paths. When creating the versioner file system, first try to expand any leading tildes to the user's home directory before handling relative paths. This makes a version path `"~/p"` expand to `"$HOME/p"` instead of `"/folder/~/p"`. ### Testing Added a test to lib/versioner that exercises this code path. Also manually tested with local syncthing instances. --- lib/versioner/simple_test.go | 66 ++++++++++++++++++++++++++++++++++++ lib/versioner/util.go | 16 +++++++-- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/lib/versioner/simple_test.go b/lib/versioner/simple_test.go index fe92478e7..3b3abb436 100644 --- a/lib/versioner/simple_test.go +++ b/lib/versioner/simple_test.go @@ -8,7 +8,9 @@ package versioner import ( "math" + "os" "path/filepath" + "strings" "testing" "time" @@ -93,3 +95,67 @@ func TestSimpleVersioningVersionCount(t *testing.T) { time.Sleep(time.Second) } } + +func TestPathTildes(t *testing.T) { + // Test that folder and version paths with leading tildes are expanded + // to the user's home directory. (issue #9241) + home := t.TempDir() + t.Setenv("HOME", home) + if vn := filepath.VolumeName(home); vn != "" { + // Legacy Windows home stuff + t.Setenv("HomeDrive", vn) + t.Setenv("HomePath", strings.TrimPrefix(home, vn)) + } + os.Mkdir(filepath.Join(home, "folder"), 0o755) + + cfg := config.FolderConfiguration{ + FilesystemType: fs.FilesystemTypeBasic, + Path: "~/folder", + Versioning: config.VersioningConfiguration{ + FSPath: "~/versions", + FSType: fs.FilesystemTypeBasic, + Params: map[string]string{ + "keep": "2", + }, + }, + } + fs := cfg.Filesystem(nil) + v := newSimple(cfg) + + const testPath = "test" + + f, err := fs.Create(testPath) + if err != nil { + t.Fatal(err) + } + f.Close() + if err := v.Archive(testPath); err != nil { + t.Fatal(err) + } + + // Check that there are no entries in the folder directory; this is + // specifically to check that there is no directory named "~" there. + names, err := fs.DirNames(".") + if err != nil { + t.Fatal(err) + } + if len(names) != 0 { + t.Fatalf("found %d files in folder dir, want 0", len(names)) + } + + // Check that the versions directory contains one file that begins with + // our test path. + des, err := os.ReadDir(filepath.Join(home, "versions")) + if err != nil { + t.Fatal(err) + } + for _, de := range des { + names = append(names, de.Name()) + } + if len(names) != 1 { + t.Fatalf("found %d files in versions dir, want 1", len(names)) + } + if got := names[0]; !strings.HasPrefix(got, testPath) { + t.Fatalf("found versioned file %q, want one that begins with %q", got, testPath) + } +} diff --git a/lib/versioner/util.go b/lib/versioner/util.go index cb7773da3..a0bcfba1e 100644 --- a/lib/versioner/util.go +++ b/lib/versioner/util.go @@ -262,10 +262,20 @@ func versionerFsFromFolderCfg(cfg config.FolderConfiguration) (versionsFs fs.Fil folderFs := cfg.Filesystem(nil) if cfg.Versioning.FSPath == "" { versionsFs = fs.NewFilesystem(folderFs.Type(), filepath.Join(folderFs.URI(), DefaultPath)) - } else if cfg.Versioning.FSType == fs.FilesystemTypeBasic && !filepath.IsAbs(cfg.Versioning.FSPath) { - // We only know how to deal with relative folders for basic filesystems, as that's the only one we know + } else if cfg.Versioning.FSType == fs.FilesystemTypeBasic { + // Expand any leading tildes for basic filesystems, + // before checking for absolute paths. + path, err := fs.ExpandTilde(cfg.Versioning.FSPath) + if err != nil { + path = cfg.Versioning.FSPath + } + // We only know how to deal with relative folders for + // basic filesystems, as that's the only one we know // how to check if it's absolute or relative. - versionsFs = fs.NewFilesystem(cfg.Versioning.FSType, filepath.Join(folderFs.URI(), cfg.Versioning.FSPath)) + if !filepath.IsAbs(path) { + path = filepath.Join(folderFs.URI(), path) + } + versionsFs = fs.NewFilesystem(cfg.Versioning.FSType, path) } else { versionsFs = fs.NewFilesystem(cfg.Versioning.FSType, cfg.Versioning.FSPath) }