@@ -9,22 +9,33 @@ import (
99 "path"
1010 "path/filepath"
1111 "strings"
12+ "sync"
13+ "time"
1214
1315 "github.com/vmware/go-nfs-client/nfs/rpc"
1416 "github.com/vmware/go-nfs-client/nfs/util"
1517 "github.com/vmware/go-nfs-client/nfs/xdr"
1618)
1719
20+ type cacheEntry struct {
21+ fh []byte
22+ attr * Fattr
23+ expire time.Time
24+ }
1825type Target struct {
1926 * rpc.Client
2027
2128 auth rpc.Auth
2229 fh []byte
2330 dirPath string
2431 fsinfo * FSInfo
32+
33+ entryTimeout time.Duration
34+ cacheM sync.Mutex
35+ entries map [string ]map [string ]* cacheEntry
2536}
2637
27- func NewTarget (addr string , auth rpc.Auth , fh []byte , dirpath string ) (* Target , error ) {
38+ func NewTarget (addr string , auth rpc.Auth , fh []byte , dirpath string , entryTimeout time. Duration ) (* Target , error ) {
2839 m := rpc.Mapping {
2940 Prog : Nfs3Prog ,
3041 Vers : Nfs3Vers ,
@@ -38,10 +49,12 @@ func NewTarget(addr string, auth rpc.Auth, fh []byte, dirpath string) (*Target,
3849 }
3950
4051 vol := & Target {
41- Client : client ,
42- auth : auth ,
43- fh : fh ,
44- dirPath : dirpath ,
52+ Client : client ,
53+ auth : auth ,
54+ fh : fh ,
55+ dirPath : dirpath ,
56+ entryTimeout : entryTimeout ,
57+ entries : make (map [string ]map [string ]* cacheEntry ),
4558 }
4659
4760 fsinfo , err := vol .FSInfo ()
@@ -51,7 +64,7 @@ func NewTarget(addr string, auth rpc.Auth, fh []byte, dirpath string) (*Target,
5164
5265 vol .fsinfo = fsinfo
5366 util .Debugf ("%s:%s fsinfo=%#v" , addr , dirpath , fsinfo )
54-
67+ go vol . cleanupCache ()
5568 return vol , nil
5669}
5770
@@ -105,6 +118,31 @@ func (v *Target) FSInfo() (*FSInfo, error) {
105118 return fsinfo , nil
106119}
107120
121+ func (v * Target ) cleanupCache () {
122+ for {
123+ v .cacheM .Lock ()
124+ now := time .Now ()
125+ var cnt int
126+ OUTER:
127+ for fh , es := range v .entries {
128+ for n , e := range es {
129+ if now .After (e .expire ) {
130+ delete (es , n )
131+ if len (es ) == 0 {
132+ delete (v .entries , fh )
133+ }
134+ }
135+ cnt ++
136+ if cnt > 1000 {
137+ break OUTER
138+ }
139+ }
140+ }
141+ v .cacheM .Unlock ()
142+ time .Sleep (time .Second )
143+ }
144+ }
145+
108146// Lookup returns attributes and the file handle to a given dirent
109147func (v * Target ) Lookup (p string ) (os.FileInfo , []byte , error ) {
110148 var (
@@ -117,22 +155,62 @@ func (v *Target) Lookup(p string) (os.FileInfo, []byte, error) {
117155 dirents := strings .Split (path .Clean (p ), "/" )
118156 for _ , dirent := range dirents {
119157 // we're assuming the root is always the root of the mount
120- if dirent == "." || dirent == " " {
158+ if dirent == "" {
121159 util .Debugf ("root -> 0x%x" , fh )
122- continue
160+ dirent = "."
123161 }
124162
125- fattr , fh , err = v .lookup (fh , dirent )
163+ fattr , fh , err = v .cachedLookup (fh , dirent )
126164 if err != nil {
127165 return nil , nil , err
128166 }
129167
130168 //util.Debugf("%s -> 0x%x", dirent, fh)
169+ // TODO: resolve symlink
131170 }
132171
133172 return fattr , fh , nil
134173}
135174
175+ func (v * Target ) parsefh (fh []byte ) string {
176+ return string (fh )
177+ }
178+
179+ func (v * Target ) cachedLookup (fh []byte , name string ) (* Fattr , []byte , error ) {
180+ ino := v .parsefh (fh )
181+ v .cacheM .Lock ()
182+ es := v .entries [ino ]
183+ if es != nil {
184+ e := es [name ]
185+ if e != nil && time .Since (e .expire ) < 0 {
186+ v .cacheM .Unlock ()
187+ return e .attr , e .fh , nil
188+ }
189+ }
190+ v .cacheM .Unlock ()
191+ attr , fh , err := v .lookup (fh , name )
192+ if err == nil && attr .Type == 2 { // only cache directories
193+ if es == nil {
194+ es = make (map [string ]* cacheEntry )
195+ v .entries [ino ] = es
196+ }
197+ v .cacheM .Lock ()
198+ es [name ] = & cacheEntry {fh , attr , time .Now ().Add (v .entryTimeout )}
199+ v .cacheM .Unlock ()
200+ }
201+ return attr , fh , err
202+ }
203+
204+ func (v * Target ) invalidateEntryCache (fh []byte , name string ) {
205+ ino := v .parsefh (fh )
206+ v .cacheM .Lock ()
207+ es , ok := v .entries [ino ]
208+ if ok {
209+ delete (es , name )
210+ }
211+ v .cacheM .Unlock ()
212+ }
213+
136214// lookup returns the same as above, but by fh and name
137215func (v * Target ) lookup (fh []byte , name string ) (* Fattr , []byte , error ) {
138216 type Lookup3Args struct {
@@ -327,7 +405,7 @@ func (v *Target) Mkdir(path string, perm os.FileMode) ([]byte, error) {
327405 util .Debugf ("mkdir(%s) partial response: %+v" , mkdirres )
328406 return nil , err
329407 }
330-
408+ v . invalidateEntryCache ( fh , newDir )
331409 util .Debugf ("mkdir(%s): created successfully (0x%x)" , path , fh )
332410 return mkdirres .FH .FH , nil
333411}
@@ -391,7 +469,7 @@ func (v *Target) Create(path string, perm os.FileMode) ([]byte, error) {
391469 if err = xdr .Read (res , status ); err != nil {
392470 return nil , err
393471 }
394-
472+ v . invalidateEntryCache ( fh , newFile )
395473 util .Debugf ("create(%s): created successfully" , path )
396474 return status .FH .FH , nil
397475}
@@ -433,7 +511,7 @@ func (v *Target) remove(fh []byte, deleteFile string) error {
433511 util .Debugf ("remove(%s): %s" , deleteFile , err .Error ())
434512 return err
435513 }
436-
514+ v . invalidateEntryCache ( fh , deleteFile )
437515 return nil
438516}
439517
@@ -474,7 +552,7 @@ func (v *Target) rmDir(fh []byte, name string) error {
474552 util .Debugf ("rmdir(%s): %s" , name , err .Error ())
475553 return err
476554 }
477-
555+ v . invalidateEntryCache ( fh , name )
478556 util .Debugf ("rmdir(%s): deleted successfully" , name )
479557 return nil
480558}
0 commit comments