mirror of
https://github.com/octoleo/syncthing.git
synced 2025-03-21 18:22:22 +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,7 +246,6 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
|
|||||||
|
|
||||||
addPattern := func(line string) error {
|
addPattern := func(line string) error {
|
||||||
pattern := Pattern{
|
pattern := Pattern{
|
||||||
pattern: line,
|
|
||||||
result: defaultResult,
|
result: defaultResult,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,6 +274,8 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
|
|||||||
line = strings.ToLower(line)
|
line = strings.ToLower(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pattern.pattern = line
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if strings.HasPrefix(line, "/") {
|
if strings.HasPrefix(line, "/") {
|
||||||
// Pattern is rooted in the current dir only
|
// 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 {
|
if dir != nil {
|
||||||
files, _ := dir.Readdirnames(-1)
|
files, _ := dir.Readdirnames(-1)
|
||||||
for _, dirFile := range files {
|
for _, dirFile := range files {
|
||||||
if defTempNamer.IsTemporary(dirFile) || (matcher != nil && matcher.Match(filepath.Join(file.Name, dirFile)).IsDeletable()) {
|
fullDirFile := filepath.Join(file.Name, dirFile)
|
||||||
osutil.InWritableDir(osutil.Remove, filepath.Join(realName, dirFile))
|
if defTempNamer.IsTemporary(dirFile) || (matcher != nil && matcher.Match(fullDirFile).IsDeletable()) {
|
||||||
|
osutil.RemoveAll(fullDirFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dir.Close()
|
dir.Close()
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/calmh/du"
|
"github.com/calmh/du"
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
"github.com/syncthing/syncthing/lib/sync"
|
||||||
@ -107,6 +108,78 @@ func Remove(path string) error {
|
|||||||
return os.Remove(path)
|
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) {
|
func ExpandTilde(path string) (string, error) {
|
||||||
if path == "~" {
|
if path == "~" {
|
||||||
return getHomeDir()
|
return getHomeDir()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user