浏览代码

Add osutil.Glob to deal with Windows (fixes #1690)

Audrius Butkevicius 10 年之前
父节点
当前提交
d12db3e7b8

+ 1 - 1
cmd/syncthing/gui.go

@@ -829,7 +829,7 @@ func restGetSystemBrowse(w http.ResponseWriter, r *http.Request) {
 	if strings.HasSuffix(current, pathSeparator) && !strings.HasSuffix(search, pathSeparator) {
 		search = search + pathSeparator
 	}
-	subdirectories, _ := filepath.Glob(search + "*")
+	subdirectories, _ := osutil.Glob(search + "*")
 	ret := make([]string, 0, 10)
 	for _, subdirectory := range subdirectories {
 		info, err := os.Stat(subdirectory)

+ 1 - 1
cmd/syncthing/main.go

@@ -1061,7 +1061,7 @@ func cleanConfigDirectory() {
 
 	for pat, dur := range patterns {
 		pat = filepath.Join(baseDirs["config"], pat)
-		files, err := filepath.Glob(pat)
+		files, err := osutil.Glob(pat)
 		if err != nil {
 			l.Infoln("Cleaning:", err)
 			continue

+ 17 - 0
internal/osutil/glob_unix.go

@@ -0,0 +1,17 @@
+// Copyright (C) 2015 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// +build !windows
+
+package osutil
+
+import (
+	"path/filepath"
+)
+
+func Glob(pattern string) (matches []string, err error) {
+	return filepath.Glob(pattern)
+}

+ 92 - 0
internal/osutil/glob_windows.go

@@ -0,0 +1,92 @@
+// Copyright (C) 2015 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// +build windows
+
+package osutil
+
+import (
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+)
+
+// Deals with https://github.com/golang/go/issues/10577
+func Glob(pattern string) (matches []string, err error) {
+	if !hasMeta(pattern) {
+		if _, err = os.Lstat(pattern); err != nil {
+			return nil, nil
+		}
+		return []string{pattern}, nil
+	}
+
+	dir, file := filepath.Split(pattern)
+	switch dir {
+	case "":
+		dir = "."
+	case string(filepath.Separator):
+		// nothing
+	default:
+		dir = dir[0 : len(dir)-1] // chop off trailing separator
+	}
+
+	if !hasMeta(dir) {
+		return glob(dir, file, nil)
+	}
+
+	var m []string
+	m, err = Glob(dir)
+	if err != nil {
+		return
+	}
+	for _, d := range m {
+		matches, err = glob(d, file, matches)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func hasMeta(path string) bool {
+	// Strip off Windows long path prefix if it exists.
+	if strings.HasPrefix(path, "\\\\?\\") {
+		path = path[4:]
+	}
+	// TODO(niemeyer): Should other magic characters be added here?
+	return strings.IndexAny(path, "*?[") >= 0
+}
+
+func glob(dir, pattern string, matches []string) (m []string, e error) {
+	m = matches
+	fi, err := os.Stat(dir)
+	if err != nil {
+		return
+	}
+	if !fi.IsDir() {
+		return
+	}
+	d, err := os.Open(dir)
+	if err != nil {
+		return
+	}
+	defer d.Close()
+
+	names, _ := d.Readdirnames(-1)
+	sort.Strings(names)
+
+	for _, n := range names {
+		matched, err := filepath.Match(pattern, n)
+		if err != nil {
+			return m, err
+		}
+		if matched {
+			m = append(m, filepath.Join(dir, n))
+		}
+	}
+	return
+}

+ 2 - 2
internal/versioner/simple.go

@@ -97,14 +97,14 @@ func (v Simple) Archive(filePath string) error {
 	}
 
 	// Glob according to the new file~timestamp.ext pattern.
-	newVersions, err := filepath.Glob(filepath.Join(dir, taggedFilename(file, TimeGlob)))
+	newVersions, err := osutil.Glob(filepath.Join(dir, taggedFilename(file, TimeGlob)))
 	if err != nil {
 		l.Warnln("globbing:", err)
 		return nil
 	}
 
 	// Also according to the old file.ext~timestamp pattern.
-	oldVersions, err := filepath.Glob(filepath.Join(dir, file+"~"+TimeGlob))
+	oldVersions, err := osutil.Glob(filepath.Join(dir, file+"~"+TimeGlob))
 	if err != nil {
 		l.Warnln("globbing:", err)
 		return nil

+ 2 - 2
internal/versioner/staggered.go

@@ -329,14 +329,14 @@ func (v Staggered) Archive(filePath string) error {
 	}
 
 	// Glob according to the new file~timestamp.ext pattern.
-	newVersions, err := filepath.Glob(filepath.Join(dir, taggedFilename(file, TimeGlob)))
+	newVersions, err := osutil.Glob(filepath.Join(dir, taggedFilename(file, TimeGlob)))
 	if err != nil {
 		l.Warnln("globbing:", err)
 		return nil
 	}
 
 	// Also according to the old file.ext~timestamp pattern.
-	oldVersions, err := filepath.Glob(filepath.Join(dir, file+"~"+TimeGlob))
+	oldVersions, err := osutil.Glob(filepath.Join(dir, file+"~"+TimeGlob))
 	if err != nil {
 		l.Warnln("globbing:", err)
 		return nil

+ 46 - 0
internal/versioner/versioner_test.go

@@ -7,8 +7,12 @@
 package versioner
 
 import (
+	"io/ioutil"
+	"math"
+	"os"
 	"path/filepath"
 	"testing"
+	"time"
 )
 
 func TestTaggedFilename(t *testing.T) {
@@ -42,3 +46,45 @@ func TestTaggedFilename(t *testing.T) {
 		}
 	}
 }
+
+func TestSimpleVersioningVersionCount(t *testing.T) {
+	if testing.Short() {
+		t.Skip("Test takes some time, skipping.")
+	}
+
+	dir, err := ioutil.TempDir("", "")
+	defer os.RemoveAll(dir)
+	if err != nil {
+		t.Error(err)
+	}
+
+	v := NewSimple("", dir, map[string]string{"keep": "2"})
+	versionDir := filepath.Join(dir, ".stversions")
+
+	path := filepath.Join(dir, "test")
+
+	for i := 1; i <= 3; i++ {
+		f, err := os.Create(path)
+		if err != nil {
+			t.Error(err)
+		}
+		f.Close()
+		v.Archive(path)
+
+		d, err := os.Open(versionDir)
+		if err != nil {
+			t.Error(err)
+		}
+		n, err := d.Readdirnames(-1)
+		if err != nil {
+			t.Error(err)
+		}
+
+		if float64(len(n)) != math.Min(float64(i), 2) {
+			t.Error("Wrong count")
+		}
+		d.Close()
+
+		time.Sleep(time.Second)
+	}
+}

+ 1 - 1
test/cli_test.go

@@ -49,7 +49,7 @@ func TestCLIReset(t *testing.T) {
 
 	// Clean up
 
-	dirs, err = filepath.Glob("*.syncthing-reset-*")
+	dirs, err = osutil.Glob("*.syncthing-reset-*")
 	if err != nil {
 		t.Fatal(err)
 	}

+ 10 - 10
test/conflict_test.go

@@ -120,7 +120,7 @@ func TestConflict(t *testing.T) {
 	// The conflict is expected on the s2 side due to how we calculate which
 	// file is the winner (based on device ID)
 
-	files, err := filepath.Glob("s2/*sync-conflict*")
+	files, err := osutil.Glob("s2/*sync-conflict*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -170,7 +170,7 @@ func TestConflict(t *testing.T) {
 	// The conflict should manifest on the s2 side again, where we should have
 	// moved the file to a conflict copy instead of just deleting it.
 
-	files, err = filepath.Glob("s2/*sync-conflict*")
+	files, err = osutil.Glob("s2/*sync-conflict*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -240,7 +240,7 @@ func TestInitialMergeConflicts(t *testing.T) {
 
 	// s1 should have three-four files (there's a conflict from s2 which may or may not have synced yet)
 
-	files, err := filepath.Glob("s1/file*")
+	files, err := osutil.Glob("s1/file*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -250,7 +250,7 @@ func TestInitialMergeConflicts(t *testing.T) {
 
 	// s2 should have four files (there's a conflict)
 
-	files, err = filepath.Glob("s2/file*")
+	files, err = osutil.Glob("s2/file*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -260,7 +260,7 @@ func TestInitialMergeConflicts(t *testing.T) {
 
 	// file1 is in conflict, so there's two versions of that one
 
-	files, err = filepath.Glob("s2/file1*")
+	files, err = osutil.Glob("s2/file1*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -316,7 +316,7 @@ func TestResetConflicts(t *testing.T) {
 
 	// s1 should have three files
 
-	files, err := filepath.Glob("s1/file*")
+	files, err := osutil.Glob("s1/file*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -326,7 +326,7 @@ func TestResetConflicts(t *testing.T) {
 
 	// s2 should have three
 
-	files, err = filepath.Glob("s2/file*")
+	files, err = osutil.Glob("s2/file*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -409,7 +409,7 @@ func TestResetConflicts(t *testing.T) {
 
 	// s2 should have five files (three plus two conflicts)
 
-	files, err = filepath.Glob("s2/file*")
+	files, err = osutil.Glob("s2/file*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -419,7 +419,7 @@ func TestResetConflicts(t *testing.T) {
 
 	// file1 is in conflict, so there's two versions of that one
 
-	files, err = filepath.Glob("s2/file1*")
+	files, err = osutil.Glob("s2/file1*")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -429,7 +429,7 @@ func TestResetConflicts(t *testing.T) {
 
 	// file2 is in conflict, so there's two versions of that one
 
-	files, err = filepath.Glob("s2/file2*")
+	files, err = osutil.Glob("s2/file2*")
 	if err != nil {
 		t.Fatal(err)
 	}

+ 1 - 1
test/util.go

@@ -241,7 +241,7 @@ func (i *inifiteReader) Read(bs []byte) (int, error) {
 // rm -rf
 func removeAll(dirs ...string) error {
 	for _, dir := range dirs {
-		files, err := filepath.Glob(dir)
+		files, err := osutil.Glob(dir)
 		if err != nil {
 			return err
 		}