diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 3e4d6ed23..8dd2b7dec 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -685,8 +685,8 @@ func restPostScan(m *model.Model, w http.ResponseWriter, r *http.Request) { qs := r.URL.Query() folder := qs.Get("folder") if folder != "" { - sub := qs.Get("sub") - err := m.ScanFolderSub(folder, sub) + subs := qs["sub"] + err := m.ScanFolderSubs(folder, subs) if err != nil { http.Error(w, err.Error(), 500) } diff --git a/internal/model/model.go b/internal/model/model.go index a756f5ec0..b7d6be512 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -1114,13 +1114,16 @@ func (m *Model) ScanFolders() map[string]error { } func (m *Model) ScanFolder(folder string) error { - return m.ScanFolderSub(folder, "") + return m.ScanFolderSubs(folder, nil) } -func (m *Model) ScanFolderSub(folder, sub string) error { - sub = osutil.NativeFilename(sub) - if p := filepath.Clean(filepath.Join(folder, sub)); !strings.HasPrefix(p, folder) { - return errors.New("invalid subpath") +func (m *Model) ScanFolderSubs(folder string, subs []string) error { + for i, sub := range subs { + sub = osutil.NativeFilename(sub) + if p := filepath.Clean(filepath.Join(folder, sub)); !strings.HasPrefix(p, folder) { + return errors.New("invalid subpath") + } + subs[i] = sub } m.fmut.Lock() @@ -1141,19 +1144,30 @@ func (m *Model) ScanFolderSub(folder, sub string) error { // Required to make sure that we start indexing at a directory we're already // aware off. - for sub != "" { - if _, ok = fs.Get(protocol.LocalDeviceID, sub); ok { - break + var unifySubs []string +nextSub: + for _, sub := range subs { + for sub != "" { + if _, ok = fs.Get(protocol.LocalDeviceID, sub); ok { + break + } + sub = filepath.Dir(sub) + if sub == "." || sub == string(filepath.Separator) { + sub = "" + } } - sub = filepath.Dir(sub) - if sub == "." || sub == string(filepath.Separator) { - sub = "" + for _, us := range unifySubs { + if strings.HasPrefix(sub, us) { + continue nextSub + } } + unifySubs = append(unifySubs, sub) } + subs = unifySubs w := &scanner.Walker{ Dir: folderCfg.Path, - Sub: sub, + Subs: subs, Matcher: ignores, BlockSize: protocol.BlockSize, TempNamer: defTempNamer, @@ -1196,10 +1210,17 @@ func (m *Model) ScanFolderSub(folder, sub string) error { seenPrefix := false fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi db.FileIntf) bool { f := fi.(db.FileInfoTruncated) - if !strings.HasPrefix(f.Name, sub) { - // Return true so that we keep iterating, until we get to the part - // of the tree we are interested in. Then return false so we stop - // iterating when we've passed the end of the subtree. + hasPrefix := len(subs) == 0 + for _, sub := range subs { + if strings.HasPrefix(f.Name, sub) { + hasPrefix = true + break + } + } + // Return true so that we keep iterating, until we get to the part + // of the tree we are interested in. Then return false so we stop + // iterating when we've passed the end of the subtree. + if !hasPrefix { return !seenPrefix } diff --git a/internal/scanner/walk.go b/internal/scanner/walk.go index bba98685a..e11a1193c 100644 --- a/internal/scanner/walk.go +++ b/internal/scanner/walk.go @@ -39,8 +39,8 @@ func init() { type Walker struct { // Dir is the base directory for the walk Dir string - // Limit walking to this path within Dir, or no limit if Sub is blank - Sub string + // Limit walking to these paths within Dir, or no limit if Sub is empty + Subs []string // BlockSize controls the size of the block used when hashing. BlockSize int // If Matcher is not nil, it is used to identify files to ignore which were specified by the user. @@ -80,7 +80,7 @@ type CurrentFiler interface { // file system. Files are blockwise hashed. func (w *Walker) Walk() (chan protocol.FileInfo, error) { if debug { - l.Debugln("Walk", w.Dir, w.Sub, w.BlockSize, w.Matcher) + l.Debugln("Walk", w.Dir, w.Subs, w.BlockSize, w.Matcher) } err := checkDir(w.Dir) @@ -99,7 +99,13 @@ func (w *Walker) Walk() (chan protocol.FileInfo, error) { go func() { hashFiles := w.walkAndHashFiles(files) - filepath.Walk(filepath.Join(w.Dir, w.Sub), hashFiles) + if len(w.Subs) == 0 { + filepath.Walk(w.Dir, hashFiles) + } else { + for _, sub := range w.Subs { + filepath.Walk(filepath.Join(w.Dir, sub), hashFiles) + } + } close(files) }() diff --git a/internal/scanner/walk_test.go b/internal/scanner/walk_test.go index 36370d945..85f862822 100644 --- a/internal/scanner/walk_test.go +++ b/internal/scanner/walk_test.go @@ -60,7 +60,7 @@ func TestWalkSub(t *testing.T) { w := Walker{ Dir: "testdata", - Sub: "dir2", + Subs: []string{"dir2"}, BlockSize: 128 * 1024, Matcher: ignores, }