github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/fs/recent.go (about) 1 // +build linux darwin 2 3 /* 4 Copyright 2013 Google Inc. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package fs 20 21 import ( 22 "log" 23 "os" 24 "path/filepath" 25 "strings" 26 "sync" 27 "time" 28 29 "camlistore.org/pkg/blob" 30 "camlistore.org/pkg/search" 31 32 "camlistore.org/third_party/bazil.org/fuse" 33 "camlistore.org/third_party/bazil.org/fuse/fs" 34 ) 35 36 // recentDir implements fuse.Node and is a directory of recent 37 // permanodes' files, for permanodes with a camliContent pointing to a 38 // "file". 39 type recentDir struct { 40 noXattr 41 fs *CamliFileSystem 42 43 mu sync.Mutex 44 ents map[string]*search.DescribedBlob // filename to blob meta 45 modTime map[string]time.Time // filename to permanode modtime 46 lastReaddir time.Time 47 lastNames []string 48 } 49 50 func (n *recentDir) Attr() fuse.Attr { 51 return fuse.Attr{ 52 Mode: os.ModeDir | 0500, 53 Uid: uint32(os.Getuid()), 54 Gid: uint32(os.Getgid()), 55 } 56 } 57 58 const recentSearchInterval = 10 * time.Second 59 60 func (n *recentDir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) { 61 var ents []fuse.Dirent 62 63 n.mu.Lock() 64 defer n.mu.Unlock() 65 if n.lastReaddir.After(time.Now().Add(-recentSearchInterval)) { 66 log.Printf("fs.recent: ReadDir from cache") 67 for _, name := range n.lastNames { 68 ents = append(ents, fuse.Dirent{Name: name}) 69 } 70 return ents, nil 71 } 72 73 log.Printf("fs.recent: ReadDir, doing search") 74 75 n.ents = make(map[string]*search.DescribedBlob) 76 n.modTime = make(map[string]time.Time) 77 78 req := &search.RecentRequest{N: 100} 79 res, err := n.fs.client.GetRecentPermanodes(req) 80 if err != nil { 81 log.Printf("fs.recent: GetRecentPermanodes error in ReadDir: %v", err) 82 return nil, fuse.EIO 83 } 84 85 n.lastNames = nil 86 for _, ri := range res.Recent { 87 modTime := ri.ModTime.Time() 88 meta := res.Meta.Get(ri.BlobRef) 89 if meta == nil || meta.Permanode == nil { 90 continue 91 } 92 cc, ok := blob.Parse(meta.Permanode.Attr.Get("camliContent")) 93 if !ok { 94 continue 95 } 96 ccMeta := res.Meta.Get(cc) 97 if ccMeta == nil { 98 continue 99 } 100 var name string 101 switch { 102 case ccMeta.File != nil: 103 name = ccMeta.File.FileName 104 if mt := ccMeta.File.Time; !mt.IsZero() { 105 modTime = mt.Time() 106 } 107 case ccMeta.Dir != nil: 108 name = ccMeta.Dir.FileName 109 default: 110 continue 111 } 112 if name == "" || n.ents[name] != nil { 113 ext := filepath.Ext(name) 114 if ext == "" && strings.HasSuffix(ccMeta.File.MIMEType, "image/jpeg") { 115 ext = ".jpg" 116 } 117 name = strings.TrimPrefix(ccMeta.BlobRef.String(), "sha1-")[:10] + ext 118 if n.ents[name] != nil { 119 continue 120 } 121 } 122 n.ents[name] = ccMeta 123 n.modTime[name] = modTime 124 log.Printf("fs.recent: name %q = %v (at %v -> %v)", name, ccMeta.BlobRef, ri.ModTime.Time(), modTime) 125 n.lastNames = append(n.lastNames, name) 126 ents = append(ents, fuse.Dirent{ 127 Name: name, 128 }) 129 } 130 log.Printf("fs.recent returning %d entries", len(ents)) 131 n.lastReaddir = time.Now() 132 return ents, nil 133 } 134 135 func (n *recentDir) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) { 136 n.mu.Lock() 137 defer n.mu.Unlock() 138 if n.ents == nil { 139 // Odd case: a Lookup before a Readdir. Force a readdir to 140 // seed our map. Mostly hit just during development. 141 n.mu.Unlock() // release, since ReadDir will acquire 142 n.ReadDir(intr) 143 n.mu.Lock() 144 } 145 db := n.ents[name] 146 log.Printf("fs.recent: Lookup(%q) = %v", name, db) 147 if db == nil { 148 return nil, fuse.ENOENT 149 } 150 nod := &node{ 151 fs: n.fs, 152 blobref: db.BlobRef, 153 pnodeModTime: n.modTime[name], 154 } 155 return nod, nil 156 }