Browse Source

Implement external scan request (fixes #9)

Jakob Borg 11 years ago
parent
commit
9752ea9ac3
4 changed files with 59 additions and 3 deletions
  1. 11 0
      cmd/syncthing/gui.go
  2. 20 1
      model/model.go
  3. 4 2
      scanner/walk.go
  4. 24 0
      scanner/walk_test.go

+ 11 - 0
cmd/syncthing/gui.go

@@ -126,6 +126,7 @@ func startGUI(cfg config.GUIConfiguration, assetDir string, m *model.Model) erro
 	postRestMux.HandleFunc("/rest/restart", restPostRestart)
 	postRestMux.HandleFunc("/rest/shutdown", restPostShutdown)
 	postRestMux.HandleFunc("/rest/upgrade", restPostUpgrade)
+	postRestMux.HandleFunc("/rest/scan", withModel(m, restPostScan))
 
 	// A handler that splits requests between the two above and disables
 	// caching
@@ -530,6 +531,16 @@ func restPostUpgrade(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+func restPostScan(m *model.Model, w http.ResponseWriter, r *http.Request) {
+	qs := r.URL.Query()
+	repo := qs.Get("repo")
+	sub := qs.Get("sub")
+	err := m.ScanRepoSub(repo, sub)
+	if err != nil {
+		http.Error(w, err.Error(), 500)
+	}
+}
+
 func getQR(w http.ResponseWriter, r *http.Request) {
 	var qs = r.URL.Query()
 	var text = qs.Get("text")

+ 20 - 1
model/model.go

@@ -12,6 +12,7 @@ import (
 	"os"
 	"path/filepath"
 	"strconv"
+	"strings"
 	"sync"
 	"time"
 
@@ -744,12 +745,21 @@ func (m *Model) CleanRepos() {
 }
 
 func (m *Model) ScanRepo(repo string) error {
+	return m.ScanRepoSub(repo, "")
+}
+
+func (m *Model) ScanRepoSub(repo, sub string) error {
+	if p := filepath.Clean(filepath.Join(repo, sub)); !strings.HasPrefix(p, repo) {
+		return errors.New("invalid subpath")
+	}
+
 	m.rmut.RLock()
-	fs := m.repoFiles[repo]
+	fs, ok := m.repoFiles[repo]
 	dir := m.repoCfgs[repo].Directory
 
 	w := &scanner.Walker{
 		Dir:          dir,
+		Sub:          sub,
 		IgnoreFile:   ".stignore",
 		BlockSize:    scanner.StandardBlockSize,
 		TempNamer:    defTempNamer,
@@ -758,6 +768,9 @@ func (m *Model) ScanRepo(repo string) error {
 		IgnorePerms:  m.repoCfgs[repo].IgnorePerms,
 	}
 	m.rmut.RUnlock()
+	if !ok {
+		return errors.New("no such repo")
+	}
 
 	m.setState(repo, RepoScanning)
 	fchan, _, err := w.Walk()
@@ -786,7 +799,13 @@ func (m *Model) ScanRepo(repo string) error {
 	}
 
 	batch = batch[:0]
+	// TODO: We should limit the Have scanning to start at sub
+	seenPrefix := false
 	fs.WithHave(protocol.LocalNodeID, func(f protocol.FileInfo) bool {
+		if !strings.HasPrefix(f.Name, sub) {
+			return !seenPrefix
+		}
+		seenPrefix = true
 		if !protocol.IsDeleted(f.Flags) {
 			if len(batch) == batchSize {
 				fs.Update(protocol.LocalNodeID, batch)

+ 4 - 2
scanner/walk.go

@@ -22,6 +22,8 @@ import (
 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
 	// BlockSize controls the size of the block used when hashing.
 	BlockSize int
 	// If IgnoreFile is not empty, it is the name used for the file that holds ignore patterns.
@@ -61,7 +63,7 @@ type CurrentFiler interface {
 // file system. Files are blockwise hashed.
 func (w *Walker) Walk() (chan protocol.FileInfo, map[string][]string, error) {
 	if debug {
-		l.Debugln("Walk", w.Dir, w.BlockSize, w.IgnoreFile)
+		l.Debugln("Walk", w.Dir, w.Sub, w.BlockSize, w.IgnoreFile)
 	}
 
 	err := checkDir(w.Dir)
@@ -77,7 +79,7 @@ func (w *Walker) Walk() (chan protocol.FileInfo, map[string][]string, error) {
 
 	go func() {
 		filepath.Walk(w.Dir, w.loadIgnoreFiles(w.Dir, ignore))
-		filepath.Walk(w.Dir, hashFiles)
+		filepath.Walk(filepath.Join(w.Dir, w.Sub), hashFiles)
 		close(files)
 	}()
 

+ 24 - 0
scanner/walk_test.go

@@ -29,6 +29,30 @@ var correctIgnores = map[string][]string{
 	".": {".*", "quux"},
 }
 
+func TestWalkSub(t *testing.T) {
+	w := Walker{
+		Dir:        "testdata",
+		Sub:        "foo",
+		BlockSize:  128 * 1024,
+		IgnoreFile: ".stignore",
+	}
+	fchan, _, err := w.Walk()
+	var files []protocol.FileInfo
+	for f := range fchan {
+		files = append(files, f)
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(files) != 1 {
+		t.Fatalf("Incorrect length %d != 1", len(files))
+	}
+	if files[0].Name != "foo" {
+		t.Errorf("Incorrect file %v != foo", files[0])
+	}
+}
+
 func TestWalk(t *testing.T) {
 	w := Walker{
 		Dir:        "testdata",