github.com/ava-labs/subnet-evm@v0.6.4/accounts/keystore/file_cache.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2017 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package keystore 28 29 import ( 30 "os" 31 "path/filepath" 32 "strings" 33 "sync" 34 "time" 35 36 mapset "github.com/deckarep/golang-set/v2" 37 "github.com/ethereum/go-ethereum/log" 38 ) 39 40 // fileCache is a cache of files seen during scan of keystore. 41 type fileCache struct { 42 all mapset.Set[string] // Set of all files from the keystore folder 43 lastMod time.Time // Last time instance when a file was modified 44 mu sync.Mutex 45 } 46 47 // scan performs a new scan on the given directory, compares against the already 48 // cached filenames, and returns file sets: creates, deletes, updates. 49 func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string], mapset.Set[string], error) { 50 t0 := time.Now() 51 52 // List all the files from the keystore folder 53 files, err := os.ReadDir(keyDir) 54 if err != nil { 55 return nil, nil, nil, err 56 } 57 t1 := time.Now() 58 59 fc.mu.Lock() 60 defer fc.mu.Unlock() 61 62 // Iterate all the files and gather their metadata 63 all := mapset.NewThreadUnsafeSet[string]() 64 mods := mapset.NewThreadUnsafeSet[string]() 65 66 var newLastMod time.Time 67 for _, fi := range files { 68 path := filepath.Join(keyDir, fi.Name()) 69 // Skip any non-key files from the folder 70 if nonKeyFile(fi) { 71 log.Trace("Ignoring file on account scan", "path", path) 72 continue 73 } 74 // Gather the set of all and freshly modified files 75 all.Add(path) 76 77 info, err := fi.Info() 78 if err != nil { 79 return nil, nil, nil, err 80 } 81 modified := info.ModTime() 82 if modified.After(fc.lastMod) { 83 mods.Add(path) 84 } 85 if modified.After(newLastMod) { 86 newLastMod = modified 87 } 88 } 89 t2 := time.Now() 90 91 // Update the tracked files and return the three sets 92 deletes := fc.all.Difference(all) // Deletes = previous - current 93 creates := all.Difference(fc.all) // Creates = current - previous 94 updates := mods.Difference(creates) // Updates = modified - creates 95 96 fc.all, fc.lastMod = all, newLastMod 97 t3 := time.Now() 98 99 // Report on the scanning stats and return 100 log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2)) 101 return creates, deletes, updates, nil 102 } 103 104 // nonKeyFile ignores editor backups, hidden files and folders/symlinks. 105 func nonKeyFile(fi os.DirEntry) bool { 106 // Skip editor backups and UNIX-style hidden files. 107 if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { 108 return true 109 } 110 // Skip misc special files, directories (yes, symlinks too). 111 if fi.IsDir() || !fi.Type().IsRegular() { 112 return true 113 } 114 return false 115 }