github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/ipnsfs/system.go (about) 1 // package ipnsfs implements an in memory model of a mutable ipns filesystem, 2 // to be used by the fuse filesystem. 3 // 4 // It consists of four main structs: 5 // 1) The Filesystem 6 // The filesystem serves as a container and entry point for the ipns filesystem 7 // 2) KeyRoots 8 // KeyRoots represent the root of the keyspace controlled by a given keypair 9 // 3) Directories 10 // 4) Files 11 package ipnsfs 12 13 import ( 14 "errors" 15 "os" 16 "sync" 17 "time" 18 19 key "github.com/ipfs/go-ipfs/blocks/key" 20 dag "github.com/ipfs/go-ipfs/merkledag" 21 namesys "github.com/ipfs/go-ipfs/namesys" 22 ci "github.com/ipfs/go-ipfs/p2p/crypto" 23 path "github.com/ipfs/go-ipfs/path" 24 pin "github.com/ipfs/go-ipfs/pin" 25 ft "github.com/ipfs/go-ipfs/unixfs" 26 27 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 28 eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" 29 ) 30 31 var log = eventlog.Logger("ipnsfs") 32 33 var ErrIsDirectory = errors.New("error: is a directory") 34 35 // Filesystem is the writeable fuse filesystem structure 36 type Filesystem struct { 37 ctx context.Context 38 39 dserv dag.DAGService 40 41 nsys namesys.NameSystem 42 43 resolver *path.Resolver 44 45 pins pin.Pinner 46 47 roots map[string]*KeyRoot 48 } 49 50 // NewFilesystem instantiates an ipns filesystem using the given parameters and locally owned keys 51 func NewFilesystem(ctx context.Context, ds dag.DAGService, nsys namesys.NameSystem, pins pin.Pinner, keys ...ci.PrivKey) (*Filesystem, error) { 52 roots := make(map[string]*KeyRoot) 53 fs := &Filesystem{ 54 ctx: ctx, 55 roots: roots, 56 nsys: nsys, 57 dserv: ds, 58 pins: pins, 59 resolver: &path.Resolver{DAG: ds}, 60 } 61 for _, k := range keys { 62 pkh, err := k.GetPublic().Hash() 63 if err != nil { 64 return nil, err 65 } 66 67 root, err := fs.newKeyRoot(ctx, k) 68 if err != nil { 69 return nil, err 70 } 71 roots[key.Key(pkh).Pretty()] = root 72 } 73 74 return fs, nil 75 } 76 77 func (fs *Filesystem) Close() error { 78 wg := sync.WaitGroup{} 79 for _, r := range fs.roots { 80 wg.Add(1) 81 go func(r *KeyRoot) { 82 defer wg.Done() 83 err := r.Publish(fs.ctx) 84 if err != nil { 85 log.Info(err) 86 return 87 } 88 }(r) 89 } 90 wg.Wait() 91 return nil 92 } 93 94 // GetRoot returns the KeyRoot of the given name 95 func (fs *Filesystem) GetRoot(name string) (*KeyRoot, error) { 96 r, ok := fs.roots[name] 97 if ok { 98 return r, nil 99 } 100 return nil, os.ErrNotExist 101 } 102 103 type childCloser interface { 104 closeChild(string, *dag.Node) error 105 } 106 107 type NodeType int 108 109 const ( 110 TFile NodeType = iota 111 TDir 112 ) 113 114 // FSNode represents any node (directory, root, or file) in the ipns filesystem 115 type FSNode interface { 116 GetNode() (*dag.Node, error) 117 Type() NodeType 118 Lock() 119 Unlock() 120 } 121 122 // KeyRoot represents the root of a filesystem tree pointed to by a given keypair 123 type KeyRoot struct { 124 key ci.PrivKey 125 name string 126 127 // node is the merkledag node pointed to by this keypair 128 node *dag.Node 129 130 // A pointer to the filesystem to access components 131 fs *Filesystem 132 133 // val represents the node pointed to by this key. It can either be a File or a Directory 134 val FSNode 135 136 repub *Republisher 137 } 138 139 // newKeyRoot creates a new KeyRoot for the given key, and starts up a republisher routine 140 // for it 141 func (fs *Filesystem) newKeyRoot(parent context.Context, k ci.PrivKey) (*KeyRoot, error) { 142 hash, err := k.GetPublic().Hash() 143 if err != nil { 144 return nil, err 145 } 146 147 name := "/ipns/" + key.Key(hash).String() 148 149 root := new(KeyRoot) 150 root.key = k 151 root.fs = fs 152 root.name = name 153 154 ctx, cancel := context.WithCancel(parent) 155 defer cancel() 156 157 pointsTo, err := fs.nsys.Resolve(ctx, name) 158 if err != nil { 159 err = namesys.InitializeKeyspace(ctx, fs.dserv, fs.nsys, fs.pins, k) 160 if err != nil { 161 return nil, err 162 } 163 164 pointsTo, err = fs.nsys.Resolve(ctx, name) 165 if err != nil { 166 return nil, err 167 } 168 } 169 170 mnode, err := fs.resolver.ResolvePath(ctx, pointsTo) 171 if err != nil { 172 log.Errorf("Failed to retrieve value '%s' for ipns entry: %s\n", pointsTo, err) 173 return nil, err 174 } 175 176 root.node = mnode 177 178 root.repub = NewRepublisher(root, time.Millisecond*300, time.Second*3) 179 go root.repub.Run(parent) 180 181 pbn, err := ft.FromBytes(mnode.Data) 182 if err != nil { 183 log.Error("IPNS pointer was not unixfs node") 184 return nil, err 185 } 186 187 switch pbn.GetType() { 188 case ft.TDirectory: 189 root.val = NewDirectory(ctx, pointsTo.String(), mnode, root, fs) 190 case ft.TFile, ft.TMetadata, ft.TRaw: 191 fi, err := NewFile(pointsTo.String(), mnode, root, fs) 192 if err != nil { 193 return nil, err 194 } 195 root.val = fi 196 default: 197 panic("unrecognized! (NYI)") 198 } 199 return root, nil 200 } 201 202 func (kr *KeyRoot) GetValue() FSNode { 203 return kr.val 204 } 205 206 // closeChild implements the childCloser interface, and signals to the publisher that 207 // there are changes ready to be published 208 func (kr *KeyRoot) closeChild(name string, nd *dag.Node) error { 209 kr.repub.Touch() 210 return nil 211 } 212 213 // Publish publishes the ipns entry associated with this key 214 func (kr *KeyRoot) Publish(ctx context.Context) error { 215 child, ok := kr.val.(FSNode) 216 if !ok { 217 return errors.New("child of key root not valid type") 218 } 219 220 nd, err := child.GetNode() 221 if err != nil { 222 return err 223 } 224 225 // Holding this lock so our child doesnt change out from under us 226 child.Lock() 227 k, err := kr.fs.dserv.Add(nd) 228 if err != nil { 229 child.Unlock() 230 return err 231 } 232 child.Unlock() 233 // Dont want to hold the lock while we publish 234 // otherwise we are holding the lock through a costly 235 // network operation 236 237 kp := path.FromKey(k) 238 239 ev := &eventlog.Metadata{"name": kr.name, "key": kp} 240 defer log.EventBegin(ctx, "ipnsfsPublishing", ev).Done() 241 log.Info("ipnsfs publishing %s -> %s", kr.name, kp) 242 243 return kr.fs.nsys.Publish(ctx, kr.key, kp) 244 } 245 246 // Republisher manages when to publish the ipns entry associated with a given key 247 type Republisher struct { 248 TimeoutLong time.Duration 249 TimeoutShort time.Duration 250 Publish chan struct{} 251 root *KeyRoot 252 } 253 254 // NewRepublisher creates a new Republisher object to republish the given keyroot 255 // using the given short and long time intervals 256 func NewRepublisher(root *KeyRoot, tshort, tlong time.Duration) *Republisher { 257 return &Republisher{ 258 TimeoutShort: tshort, 259 TimeoutLong: tlong, 260 Publish: make(chan struct{}, 1), 261 root: root, 262 } 263 } 264 265 // Touch signals that an update has occurred since the last publish. 266 // Multiple consecutive touches may extend the time period before 267 // the next Publish occurs in order to more efficiently batch updates 268 func (np *Republisher) Touch() { 269 select { 270 case np.Publish <- struct{}{}: 271 default: 272 } 273 } 274 275 // Run is the main republisher loop 276 func (np *Republisher) Run(ctx context.Context) { 277 for { 278 select { 279 case <-np.Publish: 280 quick := time.After(np.TimeoutShort) 281 longer := time.After(np.TimeoutLong) 282 283 wait: 284 select { 285 case <-ctx.Done(): 286 return 287 case <-np.Publish: 288 quick = time.After(np.TimeoutShort) 289 goto wait 290 case <-quick: 291 case <-longer: 292 } 293 294 log.Info("Publishing Changes!") 295 err := np.root.Publish(ctx) 296 if err != nil { 297 log.Error("republishRoot error: %s", err) 298 } 299 300 case <-ctx.Done(): 301 return 302 } 303 } 304 }