File: dircache.go

package info (click to toggle)
golang-github-hanwen-go-fuse 0.0~git20190214.58dcd77-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 1,076 kB
  • sloc: cpp: 78; sh: 77; makefile: 16
file content (126 lines) | stat: -rw-r--r-- 2,560 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unionfs

import (
	"sync"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/pathfs"
)

// newDirnameMap reads the contents of the given directory. On error,
// returns a nil map. This forces reloads in the dirCache until we
// succeed.
func newDirnameMap(fs pathfs.FileSystem, dir string) map[string]struct{} {
	stream, code := fs.OpenDir(dir, nil)
	if code == fuse.ENOENT {
		// The directory not existing is not an error.
		return map[string]struct{}{}
	}

	if !code.Ok() {
		return nil
	}

	result := make(map[string]struct{})
	for _, e := range stream {
		if e.Mode&fuse.S_IFREG != 0 {
			result[e.Name] = struct{}{}
		}
	}
	return result
}

// dirCache caches names in a directory for some time.
//
// If called when the cache is expired, the filenames are read afresh in
// the background.
type dirCache struct {
	dir string
	ttl time.Duration
	fs  pathfs.FileSystem
	// Protects data below.
	lock sync.RWMutex

	// If nil, you may call refresh() to schedule a new one.
	names         map[string]struct{}
	updateRunning bool
}

func (c *dirCache) setMap(newMap map[string]struct{}) {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.names = newMap
	c.updateRunning = false
	_ = time.AfterFunc(c.ttl,
		func() { c.DropCache() })
}

func (c *dirCache) DropCache() {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.names = nil
}

// Try to refresh: if another update is already running, do nothing,
// otherwise, read the directory and set it.
func (c *dirCache) maybeRefresh() {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.updateRunning {
		return
	}
	c.updateRunning = true
	go func() {
		newmap := newDirnameMap(c.fs, c.dir)
		c.setMap(newmap)
	}()
}

func (c *dirCache) RemoveEntry(name string) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.names == nil {
		go c.maybeRefresh()
		return
	}

	delete(c.names, name)
}

func (c *dirCache) AddEntry(name string) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.names == nil {
		go c.maybeRefresh()
		return
	}

	c.names[name] = struct{}{}
}

func newDirCache(fs pathfs.FileSystem, dir string, ttl time.Duration) *dirCache {
	dc := new(dirCache)
	dc.dir = dir
	dc.fs = fs
	dc.ttl = ttl
	return dc
}

func (c *dirCache) HasEntry(name string) (mapPresent bool, found bool) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	if c.names == nil {
		go c.maybeRefresh()
		return false, false
	}

	_, ok := c.names[name]
	return true, ok
}