4c98360356
Before this change we stored cached Fs under the config string the user gave us. As the result of fs.ConfigString() can often be different after the backend has canonicalised the paths this meant that we could not look up backends in the cache reliably. After this change we store cached Fs under their config string as returned from fs.ConfigString(f) after the Fs has been created. We also store a map of user to canonical names (where they are different) so the users can look up Fs under the names they passed to rclone too. This change along with Pin and Unpin is necessary so we can look up the Fs in use reliably in the `backend/command` remote control interface.
95 lines
2.4 KiB
Go
95 lines
2.4 KiB
Go
// Package cache implements the Fs cache
|
|
package cache
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/lib/cache"
|
|
)
|
|
|
|
var (
|
|
c = cache.New()
|
|
mu sync.Mutex // mutex to protect remap
|
|
remap = map[string]string{} // map user supplied names to canonical names
|
|
)
|
|
|
|
// Lookup fsString in the mapping from user supplied names to
|
|
// canonical names and return the canonical form
|
|
func canonicalize(fsString string) string {
|
|
mu.Lock()
|
|
canonicalName, ok := remap[fsString]
|
|
mu.Unlock()
|
|
if !ok {
|
|
return fsString
|
|
}
|
|
fs.Debugf(nil, "fs cache: switching user supplied name %q for canonical name %q", fsString, canonicalName)
|
|
return canonicalName
|
|
}
|
|
|
|
// Put in a mapping from fsString => canonicalName if they are different
|
|
func addMapping(fsString, canonicalName string) {
|
|
if canonicalName == fsString {
|
|
return
|
|
}
|
|
mu.Lock()
|
|
remap[fsString] = canonicalName
|
|
mu.Unlock()
|
|
}
|
|
|
|
// GetFn gets a fs.Fs named fsString either from the cache or creates
|
|
// it afresh with the create function
|
|
func GetFn(fsString string, create func(fsString string) (fs.Fs, error)) (f fs.Fs, err error) {
|
|
fsString = canonicalize(fsString)
|
|
created := false
|
|
value, err := c.Get(fsString, func(fsString string) (f interface{}, ok bool, err error) {
|
|
f, err = create(fsString)
|
|
ok = err == nil || err == fs.ErrorIsFile
|
|
created = ok
|
|
return f, ok, err
|
|
})
|
|
if err != nil && err != fs.ErrorIsFile {
|
|
return nil, err
|
|
}
|
|
f = value.(fs.Fs)
|
|
// Check we stored the Fs at the canonical name
|
|
if created {
|
|
canonicalName := fs.ConfigString(f)
|
|
if canonicalName != fsString {
|
|
fs.Debugf(nil, "fs cache: renaming cache item %q to be canonical %q", fsString, canonicalName)
|
|
value, found := c.Rename(fsString, canonicalName)
|
|
if found {
|
|
f = value.(fs.Fs)
|
|
}
|
|
addMapping(fsString, canonicalName)
|
|
}
|
|
}
|
|
return f, err
|
|
}
|
|
|
|
// Pin f into the cache until Unpin is called
|
|
func Pin(f fs.Fs) {
|
|
c.Pin(fs.ConfigString(f))
|
|
}
|
|
|
|
// Unpin f from the cache
|
|
func Unpin(f fs.Fs) {
|
|
c.Pin(fs.ConfigString(f))
|
|
}
|
|
|
|
// Get gets a fs.Fs named fsString either from the cache or creates it afresh
|
|
func Get(fsString string) (f fs.Fs, err error) {
|
|
return GetFn(fsString, fs.NewFs)
|
|
}
|
|
|
|
// Put puts an fs.Fs named fsString into the cache
|
|
func Put(fsString string, f fs.Fs) {
|
|
canonicalName := fs.ConfigString(f)
|
|
c.Put(canonicalName, f)
|
|
addMapping(fsString, canonicalName)
|
|
}
|
|
|
|
// Clear removes everything from the cahce
|
|
func Clear() {
|
|
c.Clear()
|
|
}
|