mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-10 15:20:56 +00:00
bea3c01772
Given that we've taken on the resposibility of maintaining this forked package I've added it to the Syncthing organization. We still vendor it like an external package, because it's convenient to keep it as a fork of upstream to easier merge and file pull requests towards them.
117 lines
3.3 KiB
Go
117 lines
3.3 KiB
Go
// Copyright (C) 2016 The Syncthing Authors.
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
// +build !solaris,!darwin solaris,cgo darwin,cgo
|
|
|
|
package fs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"path/filepath"
|
|
|
|
"github.com/syncthing/notify"
|
|
)
|
|
|
|
// Notify does not block on sending to channel, so the channel must be buffered.
|
|
// The actual number is magic.
|
|
// Not meant to be changed, but must be changeable for tests
|
|
var backendBuffer = 500
|
|
|
|
func (f *BasicFilesystem) Watch(name string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, error) {
|
|
absName, err := f.rooted(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
absShouldIgnore := func(absPath string) bool {
|
|
return ignore.ShouldIgnore(f.unrootedChecked(absPath))
|
|
}
|
|
|
|
outChan := make(chan Event)
|
|
backendChan := make(chan notify.EventInfo, backendBuffer)
|
|
|
|
eventMask := subEventMask
|
|
if !ignorePerms {
|
|
eventMask |= permEventMask
|
|
}
|
|
|
|
if err := notify.WatchWithFilter(filepath.Join(absName, "..."), backendChan, absShouldIgnore, eventMask); err != nil {
|
|
notify.Stop(backendChan)
|
|
if reachedMaxUserWatches(err) {
|
|
err = errors.New("failed to install inotify handler. Please increase inotify limits, see https://github.com/syncthing/syncthing-inotify#troubleshooting-for-folders-with-many-files-on-linux for more information")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
go f.watchLoop(name, absName, backendChan, outChan, ignore, ctx)
|
|
|
|
return outChan, nil
|
|
}
|
|
|
|
func (f *BasicFilesystem) watchLoop(name string, absName string, backendChan chan notify.EventInfo, outChan chan<- Event, ignore Matcher, ctx context.Context) {
|
|
for {
|
|
// Detect channel overflow
|
|
if len(backendChan) == backendBuffer {
|
|
outer:
|
|
for {
|
|
select {
|
|
case <-backendChan:
|
|
default:
|
|
break outer
|
|
}
|
|
}
|
|
// When next scheduling a scan, do it on the entire folder as events have been lost.
|
|
outChan <- Event{Name: name, Type: NonRemove}
|
|
l.Debugln(f.Type(), f.URI(), "Watch: Event overflow, send \".\"")
|
|
}
|
|
|
|
select {
|
|
case ev := <-backendChan:
|
|
relPath := f.unrootedChecked(ev.Path())
|
|
if ignore.ShouldIgnore(relPath) {
|
|
l.Debugln(f.Type(), f.URI(), "Watch: Ignoring", relPath)
|
|
continue
|
|
}
|
|
evType := f.eventType(ev.Event())
|
|
select {
|
|
case outChan <- Event{Name: relPath, Type: evType}:
|
|
l.Debugln(f.Type(), f.URI(), "Watch: Sending", relPath, evType)
|
|
case <-ctx.Done():
|
|
notify.Stop(backendChan)
|
|
l.Debugln(f.Type(), f.URI(), "Watch: Stopped")
|
|
return
|
|
}
|
|
case <-ctx.Done():
|
|
notify.Stop(backendChan)
|
|
l.Debugln(f.Type(), f.URI(), "Watch: Stopped")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *BasicFilesystem) eventType(notifyType notify.Event) EventType {
|
|
if notifyType&rmEventMask != 0 {
|
|
return Remove
|
|
}
|
|
return NonRemove
|
|
}
|
|
|
|
// unrootedChecked returns the path relative to the folder root (same as
|
|
// unrooted). It panics if the given path is not a subpath and handles the
|
|
// special case when the given path is the folder root without a trailing
|
|
// pathseparator.
|
|
func (f *BasicFilesystem) unrootedChecked(absPath string) string {
|
|
if absPath+string(PathSeparator) == f.root {
|
|
return "."
|
|
}
|
|
relPath := f.unrooted(absPath)
|
|
if relPath == absPath {
|
|
panic("bug: Notify backend is processing a change outside of the watched path: " + absPath)
|
|
}
|
|
return relPath
|
|
}
|