From 5c27796471d2da8e9dd890b476361941c4447e5b Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sat, 18 Mar 2017 00:25:47 +0000 Subject: [PATCH] lib/model, lib/scanner: Properly ignore symlinks on Windows (fixes #4035) Adds a unit test to ensure we don't scan symlinks on Windows. For the rwfolder, trusts that the logic in the invalid check is correct and that the check is actually called from the need loop. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4042 --- lib/model/rwfolder.go | 8 ++++++-- lib/scanner/walk.go | 6 ++++++ lib/scanner/walk_test.go | 41 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/lib/model/rwfolder.go b/lib/model/rwfolder.go index 8276a5acc..ab9a4372a 100644 --- a/lib/model/rwfolder.go +++ b/lib/model/rwfolder.go @@ -53,8 +53,9 @@ type copyBlocksState struct { const retainBits = os.ModeSetgid | os.ModeSetuid | os.ModeSticky var ( - activity = newDeviceActivity() - errNoDevice = errors.New("peers who had this file went away, or the file has changed while syncing. will retry later") + activity = newDeviceActivity() + errNoDevice = errors.New("peers who had this file went away, or the file has changed while syncing. will retry later") + errSymlinksUnsupported = errors.New("symlinks not supported") ) const ( @@ -1759,6 +1760,9 @@ func fileValid(file db.FileIntf) error { // We don't care about file validity if we're not supposed to have it return nil + case runtime.GOOS == "windows" && file.IsSymlink(): + return errSymlinksUnsupported + case runtime.GOOS == "windows" && windowsInvalidFilename(file.FileName()): return errInvalidFilename } diff --git a/lib/scanner/walk.go b/lib/scanner/walk.go index 8925d5271..dded1e879 100644 --- a/lib/scanner/walk.go +++ b/lib/scanner/walk.go @@ -373,6 +373,12 @@ func (w *walker) walkDir(relPath string, info os.FileInfo, dchan chan protocol.F // walkSymlink returns nil or an error, if the error is of the nature that // it should stop the entire walk. func (w *walker) walkSymlink(absPath, relPath string, dchan chan protocol.FileInfo) error { + // Symlinks are not supported on Windows. We ignore instead of returning + // an error. + if runtime.GOOS == "windows" { + return nil + } + // We always rehash symlinks as they have no modtime or // permissions. We check if they point to the old target by // checking that their existing blocks match with the blocks in diff --git a/lib/scanner/walk_test.go b/lib/scanner/walk_test.go index cfcaa74de..a65b68066 100644 --- a/lib/scanner/walk_test.go +++ b/lib/scanner/walk_test.go @@ -280,7 +280,7 @@ func TestIssue1507(t *testing.T) { fn("", nil, protocol.ErrClosed) } -func TestWalkSymlink(t *testing.T) { +func TestWalkSymlinkUnix(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping unsupported symlink test") return @@ -323,6 +323,45 @@ func TestWalkSymlink(t *testing.T) { } } +func TestWalkSymlinkWindows(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("skipping unsupported symlink test") + } + + // Create a folder with a symlink in it + + os.RemoveAll("_symlinks") + defer os.RemoveAll("_symlinks") + + os.Mkdir("_symlinks", 0755) + if err := os.Symlink("destination", "_symlinks/link"); err != nil { + // Probably we require permissions we don't have. + t.Skip(err) + } + + // Scan it + + fchan, err := Walk(Config{ + Dir: "_symlinks", + BlockSize: 128 * 1024, + }) + + if err != nil { + t.Fatal(err) + } + + var files []protocol.FileInfo + for f := range fchan { + files = append(files, f) + } + + // Verify that we got zero symlinks + + if len(files) != 0 { + t.Errorf("expected zero symlinks, not %d", len(files)) + } +} + func walkDir(dir string) ([]protocol.FileInfo, error) { fchan, err := Walk(Config{ Dir: dir,