浏览代码

lib/model: Handle (?d) deletes of directories (fixes #3164)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3170
Audrius Butkevicius 9 年之前
父节点
当前提交
915e1ac7de
共有 4 个文件被更改,包括 117 次插入4 次删除
  1. 3 2
      lib/ignore/ignore.go
  2. 38 0
      lib/ignore/ignore_test.go
  3. 3 2
      lib/model/rwfolder.go
  4. 73 0
      lib/osutil/osutil.go

+ 3 - 2
lib/ignore/ignore.go

@@ -246,8 +246,7 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
 
 	addPattern := func(line string) error {
 		pattern := Pattern{
-			pattern: line,
-			result:  defaultResult,
+			result: defaultResult,
 		}
 
 		// Allow prefixes to be specified in any order, but only once.
@@ -275,6 +274,8 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
 			line = strings.ToLower(line)
 		}
 
+		pattern.pattern = line
+
 		var err error
 		if strings.HasPrefix(line, "/") {
 			// Pattern is rooted in the current dir only

+ 38 - 0
lib/ignore/ignore_test.go

@@ -665,3 +665,41 @@ func TestCommas(t *testing.T) {
 		}
 	}
 }
+
+func TestIssue3164(t *testing.T) {
+	stignore := `
+	(?d)(?i)*.part
+	(?d)(?i)/foo
+	(?d)(?i)**/bar
+	`
+	pats := New(true)
+	err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expanded := pats.Patterns()
+	t.Log(expanded)
+	expected := []string{
+		"(?d)(?i)*.part",
+		"(?d)(?i)**/*.part",
+		"(?d)(?i)*.part/**",
+		"(?d)(?i)**/*.part/**",
+		"(?d)(?i)/foo",
+		"(?d)(?i)/foo/**",
+		"(?d)(?i)**/bar",
+		"(?d)(?i)bar",
+		"(?d)(?i)**/bar/**",
+		"(?d)(?i)bar/**",
+	}
+
+	if len(expanded) != len(expected) {
+		t.Errorf("Unmatched count: %d != %d", len(expanded), len(expected))
+	}
+
+	for i := range expanded {
+		if expanded[i] != expected[i] {
+			t.Errorf("Pattern %d does not match: %s != %s", i, expanded[i], expected[i])
+		}
+	}
+}

+ 3 - 2
lib/model/rwfolder.go

@@ -676,8 +676,9 @@ func (f *rwFolder) deleteDir(file protocol.FileInfo, matcher *ignore.Matcher) {
 	if dir != nil {
 		files, _ := dir.Readdirnames(-1)
 		for _, dirFile := range files {
-			if defTempNamer.IsTemporary(dirFile) || (matcher != nil && matcher.Match(filepath.Join(file.Name, dirFile)).IsDeletable()) {
-				osutil.InWritableDir(osutil.Remove, filepath.Join(realName, dirFile))
+			fullDirFile := filepath.Join(file.Name, dirFile)
+			if defTempNamer.IsTemporary(dirFile) || (matcher != nil && matcher.Match(fullDirFile).IsDeletable()) {
+				osutil.RemoveAll(fullDirFile)
 			}
 		}
 		dir.Close()

+ 73 - 0
lib/osutil/osutil.go

@@ -15,6 +15,7 @@ import (
 	"path/filepath"
 	"runtime"
 	"strings"
+	"syscall"
 
 	"github.com/calmh/du"
 	"github.com/syncthing/syncthing/lib/sync"
@@ -107,6 +108,78 @@ func Remove(path string) error {
 	return os.Remove(path)
 }
 
+// RemoveAll is a copy of os.RemoveAll, but uses osutil.Remove.
+// RemoveAll removes path and any children it contains.
+// It removes everything it can but returns the first error
+// it encounters.  If the path does not exist, RemoveAll
+// returns nil (no error).
+func RemoveAll(path string) error {
+	// Simple case: if Remove works, we're done.
+	err := Remove(path)
+	if err == nil || os.IsNotExist(err) {
+		return nil
+	}
+
+	// Otherwise, is this a directory we need to recurse into?
+	dir, serr := os.Lstat(path)
+	if serr != nil {
+		if serr, ok := serr.(*os.PathError); ok && (os.IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
+			return nil
+		}
+		return serr
+	}
+	if !dir.IsDir() {
+		// Not a directory; return the error from Remove.
+		return err
+	}
+
+	// Directory.
+	fd, err := os.Open(path)
+	if err != nil {
+		if os.IsNotExist(err) {
+			// Race. It was deleted between the Lstat and Open.
+			// Return nil per RemoveAll's docs.
+			return nil
+		}
+		return err
+	}
+
+	// Remove contents & return first error.
+	err = nil
+	for {
+		names, err1 := fd.Readdirnames(100)
+		for _, name := range names {
+			err1 := RemoveAll(path + string(os.PathSeparator) + name)
+			if err == nil {
+				err = err1
+			}
+		}
+		if err1 == io.EOF {
+			break
+		}
+		// If Readdirnames returned an error, use it.
+		if err == nil {
+			err = err1
+		}
+		if len(names) == 0 {
+			break
+		}
+	}
+
+	// Close directory, because windows won't remove opened directory.
+	fd.Close()
+
+	// Remove directory.
+	err1 := Remove(path)
+	if err1 == nil || os.IsNotExist(err1) {
+		return nil
+	}
+	if err == nil {
+		err = err1
+	}
+	return err
+}
+
 func ExpandTilde(path string) (string, error) {
 	if path == "~" {
 		return getHomeDir()