mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-12 16:26:37 +00:00
lib/model, lib/ignores: Properly handle out of folder ignores and free space checks (fixes #4313, fixes #4314)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4318
This commit is contained in:
parent
b8c249cddc
commit
0a96a1150b
@ -70,8 +70,8 @@ func (r Result) IsCaseFolded() bool {
|
|||||||
// called on it) and if any of the files have Changed(). To forget all
|
// called on it) and if any of the files have Changed(). To forget all
|
||||||
// files, call Reset().
|
// files, call Reset().
|
||||||
type ChangeDetector interface {
|
type ChangeDetector interface {
|
||||||
Remember(name string, modtime time.Time)
|
Remember(fs fs.Filesystem, name string, modtime time.Time)
|
||||||
Seen(name string) bool
|
Seen(fs fs.Filesystem, name string) bool
|
||||||
Changed() bool
|
Changed() bool
|
||||||
Reset()
|
Reset()
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ func New(fs fs.Filesystem, opts ...Option) *Matcher {
|
|||||||
opt(m)
|
opt(m)
|
||||||
}
|
}
|
||||||
if m.changeDetector == nil {
|
if m.changeDetector == nil {
|
||||||
m.changeDetector = newModtimeChecker(fs)
|
m.changeDetector = newModtimeChecker()
|
||||||
}
|
}
|
||||||
if m.withCache {
|
if m.withCache {
|
||||||
go m.clean(2 * time.Hour)
|
go m.clean(2 * time.Hour)
|
||||||
@ -128,7 +128,7 @@ func (m *Matcher) Load(file string) error {
|
|||||||
m.mut.Lock()
|
m.mut.Lock()
|
||||||
defer m.mut.Unlock()
|
defer m.mut.Unlock()
|
||||||
|
|
||||||
if m.changeDetector.Seen(file) && !m.changeDetector.Changed() {
|
if m.changeDetector.Seen(m.fs, file) && !m.changeDetector.Changed() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ func (m *Matcher) Load(file string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.changeDetector.Reset()
|
m.changeDetector.Reset()
|
||||||
m.changeDetector.Remember(file, info.ModTime())
|
m.changeDetector.Remember(m.fs, file, info.ModTime())
|
||||||
|
|
||||||
return m.parseLocked(fd, file)
|
return m.parseLocked(fd, file)
|
||||||
}
|
}
|
||||||
@ -300,12 +300,23 @@ func hashPatterns(patterns []Pattern) string {
|
|||||||
return fmt.Sprintf("%x", h.Sum(nil))
|
return fmt.Sprintf("%x", h.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadIgnoreFile(fs fs.Filesystem, file string, cd ChangeDetector) ([]string, []Pattern, error) {
|
func loadIgnoreFile(filesystem fs.Filesystem, file string, cd ChangeDetector) ([]string, []Pattern, error) {
|
||||||
if cd.Seen(file) {
|
if cd.Seen(filesystem, file) {
|
||||||
return nil, nil, fmt.Errorf("multiple include of ignore file %q", file)
|
return nil, nil, fmt.Errorf("multiple include of ignore file %q", file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := fs.Open(file)
|
// Allow escaping the folders filesystem.
|
||||||
|
// TODO: Deprecate, somehow?
|
||||||
|
if filesystem.Type() == fs.FilesystemTypeBasic {
|
||||||
|
uri := filesystem.URI()
|
||||||
|
joined := filepath.Join(uri, file)
|
||||||
|
if !strings.HasPrefix(joined, uri) {
|
||||||
|
filesystem = fs.NewFilesystem(filesystem.Type(), filepath.Dir(joined))
|
||||||
|
file = filepath.Base(joined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := filesystem.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -316,9 +327,9 @@ func loadIgnoreFile(fs fs.Filesystem, file string, cd ChangeDetector) ([]string,
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cd.Remember(file, info.ModTime())
|
cd.Remember(filesystem, file, info.ModTime())
|
||||||
|
|
||||||
return parseIgnoreFile(fs, fd, file, cd)
|
return parseIgnoreFile(filesystem, fd, file, cd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIgnoreFile(fs fs.Filesystem, fd io.Reader, currentFile string, cd ChangeDetector) ([]string, []Pattern, error) {
|
func parseIgnoreFile(fs fs.Filesystem, fd io.Reader, currentFile string, cd ChangeDetector) ([]string, []Pattern, error) {
|
||||||
@ -386,7 +397,7 @@ func parseIgnoreFile(fs fs.Filesystem, fd io.Reader, currentFile string, cd Chan
|
|||||||
}
|
}
|
||||||
patterns = append(patterns, pattern)
|
patterns = append(patterns, pattern)
|
||||||
} else if strings.HasPrefix(line, "#include ") {
|
} else if strings.HasPrefix(line, "#include ") {
|
||||||
includeRel := line[len("#include "):]
|
includeRel := strings.TrimSpace(line[len("#include "):])
|
||||||
includeFile := filepath.Join(filepath.Dir(currentFile), includeRel)
|
includeFile := filepath.Join(filepath.Dir(currentFile), includeRel)
|
||||||
_, includePatterns, err := loadIgnoreFile(fs, includeFile, cd)
|
_, includePatterns, err := loadIgnoreFile(fs, includeFile, cd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -483,35 +494,38 @@ func WriteIgnores(filesystem fs.Filesystem, path string, content []string) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type modtimeCheckerKey struct {
|
||||||
|
fs fs.Filesystem
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
// modtimeChecker is the default implementation of ChangeDetector
|
// modtimeChecker is the default implementation of ChangeDetector
|
||||||
type modtimeChecker struct {
|
type modtimeChecker struct {
|
||||||
fs fs.Filesystem
|
modtimes map[modtimeCheckerKey]time.Time
|
||||||
modtimes map[string]time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newModtimeChecker(fs fs.Filesystem) *modtimeChecker {
|
func newModtimeChecker() *modtimeChecker {
|
||||||
return &modtimeChecker{
|
return &modtimeChecker{
|
||||||
fs: fs,
|
modtimes: map[modtimeCheckerKey]time.Time{},
|
||||||
modtimes: map[string]time.Time{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *modtimeChecker) Remember(name string, modtime time.Time) {
|
func (c *modtimeChecker) Remember(fs fs.Filesystem, name string, modtime time.Time) {
|
||||||
c.modtimes[name] = modtime
|
c.modtimes[modtimeCheckerKey{fs, name}] = modtime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *modtimeChecker) Seen(name string) bool {
|
func (c *modtimeChecker) Seen(fs fs.Filesystem, name string) bool {
|
||||||
_, ok := c.modtimes[name]
|
_, ok := c.modtimes[modtimeCheckerKey{fs, name}]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *modtimeChecker) Reset() {
|
func (c *modtimeChecker) Reset() {
|
||||||
c.modtimes = map[string]time.Time{}
|
c.modtimes = map[modtimeCheckerKey]time.Time{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *modtimeChecker) Changed() bool {
|
func (c *modtimeChecker) Changed() bool {
|
||||||
for name, modtime := range c.modtimes {
|
for key, modtime := range c.modtimes {
|
||||||
info, err := c.fs.Stat(name)
|
info, err := key.fs.Stat(key.name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -2304,7 +2304,7 @@ func (m *Model) checkFreeSpace(req config.Size, fs fs.Filesystem) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if req.Percentage() {
|
if req.Percentage() {
|
||||||
freePct := (1 - float64(usage.Free)/float64(usage.Total)) * 100
|
freePct := (float64(usage.Free) / float64(usage.Total)) * 100
|
||||||
if err == nil && freePct < val {
|
if err == nil && freePct < val {
|
||||||
return fmt.Errorf("insufficient space in %v %v: %f %% < %v", fs.Type(), fs.URI(), freePct, req)
|
return fmt.Errorf("insufficient space in %v %v: %f %% < %v", fs.Type(), fs.URI(), freePct, req)
|
||||||
}
|
}
|
||||||
|
@ -2382,27 +2382,32 @@ func (fakeAddr) String() string {
|
|||||||
return "address"
|
return "address"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type alwaysChangedKey struct {
|
||||||
|
fs fs.Filesystem
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
// alwaysChanges is an ignore.ChangeDetector that always returns true on Changed()
|
// alwaysChanges is an ignore.ChangeDetector that always returns true on Changed()
|
||||||
type alwaysChanged struct {
|
type alwaysChanged struct {
|
||||||
seen map[string]struct{}
|
seen map[alwaysChangedKey]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAlwaysChanged() *alwaysChanged {
|
func newAlwaysChanged() *alwaysChanged {
|
||||||
return &alwaysChanged{
|
return &alwaysChanged{
|
||||||
seen: make(map[string]struct{}),
|
seen: make(map[alwaysChangedKey]struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *alwaysChanged) Remember(name string, _ time.Time) {
|
func (c *alwaysChanged) Remember(fs fs.Filesystem, name string, _ time.Time) {
|
||||||
c.seen[name] = struct{}{}
|
c.seen[alwaysChangedKey{fs, name}] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *alwaysChanged) Reset() {
|
func (c *alwaysChanged) Reset() {
|
||||||
c.seen = make(map[string]struct{})
|
c.seen = make(map[alwaysChangedKey]struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *alwaysChanged) Seen(name string) bool {
|
func (c *alwaysChanged) Seen(fs fs.Filesystem, name string) bool {
|
||||||
_, ok := c.seen[name]
|
_, ok := c.seen[alwaysChangedKey{fs, name}]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user