mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 14:50:56 +00:00
lib/model: Handle (?d) deletes of directories (fixes #3164)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3170
This commit is contained in:
parent
b78bfc0a43
commit
915e1ac7de
@ -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
|
||||
|
@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user