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

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3170
This commit is contained in:
Audrius Butkevicius 2016-05-23 23:32:08 +00:00 committed by Jakob Borg
parent b78bfc0a43
commit 915e1ac7de
4 changed files with 117 additions and 4 deletions

View File

@ -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

View File

@ -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])
}
}
}

View File

@ -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()

View File

@ -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()