package locking import ( "strings" "github.com/git-lfs/git-lfs/v3/tools/kv" ) const ( // We want to use a single cache file for integrity, but to make it easy to // list all locks, prefix the id->path map in a way we can identify (something // that won't be in a path) idKeyPrefix string = "*id*://" ) type LockCache struct { kv *kv.Store } func NewLockCache(filepath string) (*LockCache, error) { kv, err := kv.NewStore(filepath) if err != nil { return nil, err } return &LockCache{kv}, nil } // Cache a successful lock for faster local lookup later func (c *LockCache) Add(l Lock) error { // Store reference in both directions // Path -> Lock c.kv.Set(l.Path, &l) // EncodedId -> Lock (encoded so we can easily identify) c.kv.Set(c.encodeIdKey(l.Id), &l) return nil } // Remove a cached lock by path because it's been relinquished func (c *LockCache) RemoveByPath(filePath string) error { ilock := c.kv.Get(filePath) if lock, ok := ilock.(*Lock); ok && lock != nil { c.kv.Remove(lock.Path) // Id as key is encoded c.kv.Remove(c.encodeIdKey(lock.Id)) } return nil } // Remove a cached lock by id because it's been relinquished func (c *LockCache) RemoveById(id string) error { // Id as key is encoded idkey := c.encodeIdKey(id) ilock := c.kv.Get(idkey) if lock, ok := ilock.(*Lock); ok && lock != nil { c.kv.Remove(idkey) c.kv.Remove(lock.Path) } return nil } // Get the list of cached locked files func (c *LockCache) Locks() []Lock { var locks []Lock c.kv.Visit(func(key string, val interface{}) bool { // Only report file->id entries not reverse if !c.isIdKey(key) { lock := val.(*Lock) locks = append(locks, *lock) } return true // continue }) return locks } // Clear the cache func (c *LockCache) Clear() { c.kv.RemoveAll() } // Save the cache func (c *LockCache) Save() error { return c.kv.Save() } func (c *LockCache) encodeIdKey(id string) string { // Safety against accidents if !c.isIdKey(id) { return idKeyPrefix + id } return id } func (c *LockCache) decodeIdKey(key string) string { // Safety against accidents if c.isIdKey(key) { return key[len(idKeyPrefix):] } return key } func (c *LockCache) isIdKey(key string) bool { return strings.HasPrefix(key, idKeyPrefix) }