github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/fuse/ipns/ipns_unix.go (about) 1 // package fuse/ipns implements a fuse filesystem that interfaces 2 // with ipns, the naming system for ipfs. 3 package ipns 4 5 import ( 6 "errors" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "time" 11 12 fuse "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse" 13 fs "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs" 14 proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" 15 16 core "github.com/jbenet/go-ipfs/core" 17 ci "github.com/jbenet/go-ipfs/crypto" 18 chunk "github.com/jbenet/go-ipfs/importer/chunk" 19 mdag "github.com/jbenet/go-ipfs/merkledag" 20 ft "github.com/jbenet/go-ipfs/unixfs" 21 uio "github.com/jbenet/go-ipfs/unixfs/io" 22 ftpb "github.com/jbenet/go-ipfs/unixfs/pb" 23 u "github.com/jbenet/go-ipfs/util" 24 ) 25 26 var log = u.Logger("ipns") 27 28 var ( 29 shortRepublishTimeout = time.Millisecond * 5 30 longRepublishTimeout = time.Millisecond * 500 31 ) 32 33 // FileSystem is the readwrite IPNS Fuse Filesystem. 34 type FileSystem struct { 35 Ipfs *core.IpfsNode 36 RootNode *Root 37 } 38 39 // NewFileSystem constructs new fs using given core.IpfsNode instance. 40 func NewIpns(ipfs *core.IpfsNode, ipfspath string) (*FileSystem, error) { 41 root, err := CreateRoot(ipfs, []ci.PrivKey{ipfs.Identity.PrivKey()}, ipfspath) 42 if err != nil { 43 return nil, err 44 } 45 return &FileSystem{Ipfs: ipfs, RootNode: root}, nil 46 } 47 48 func CreateRoot(n *core.IpfsNode, keys []ci.PrivKey, ipfsroot string) (*Root, error) { 49 root := new(Root) 50 root.LocalDirs = make(map[string]*Node) 51 root.Ipfs = n 52 abspath, err := filepath.Abs(ipfsroot) 53 if err != nil { 54 return nil, err 55 } 56 root.IpfsRoot = abspath 57 58 root.Keys = keys 59 60 if len(keys) == 0 { 61 log.Warning("No keys given for ipns root creation") 62 } else { 63 k := keys[0] 64 pub := k.GetPublic() 65 hash, err := pub.Hash() 66 if err != nil { 67 log.Errorf("Read Root Error: %s", err) 68 return nil, err 69 } 70 root.LocalLink = &Link{u.Key(hash).Pretty()} 71 } 72 73 for _, k := range keys { 74 hash, err := k.GetPublic().Hash() 75 if err != nil { 76 log.Error("failed to hash public key.") 77 continue 78 } 79 name := u.Key(hash).Pretty() 80 nd := new(Node) 81 nd.Ipfs = n 82 nd.key = k 83 nd.repub = NewRepublisher(nd, shortRepublishTimeout, longRepublishTimeout) 84 85 go nd.repub.Run() 86 87 pointsTo, err := n.Namesys.Resolve(name) 88 if err != nil { 89 log.Warning("Could not resolve value for local ipns entry, providing empty dir") 90 nd.Nd = &mdag.Node{Data: ft.FolderPBData()} 91 root.LocalDirs[name] = nd 92 continue 93 } 94 95 if !u.IsValidHash(pointsTo) { 96 log.Criticalf("Got back bad data from namesys resolve! [%s]", pointsTo) 97 return nil, nil 98 } 99 100 node, err := n.Resolver.ResolvePath(pointsTo) 101 if err != nil { 102 log.Warning("Failed to resolve value from ipns entry in ipfs") 103 continue 104 } 105 106 nd.Nd = node 107 root.LocalDirs[name] = nd 108 } 109 110 return root, nil 111 } 112 113 // Root constructs the Root of the filesystem, a Root object. 114 func (f FileSystem) Root() (fs.Node, fuse.Error) { 115 return f.RootNode, nil 116 } 117 118 // Root is the root object of the filesystem tree. 119 type Root struct { 120 Ipfs *core.IpfsNode 121 Keys []ci.PrivKey 122 123 // Used for symlinking into ipfs 124 IpfsRoot string 125 LocalDirs map[string]*Node 126 127 LocalLink *Link 128 } 129 130 // Attr returns file attributes. 131 func (*Root) Attr() fuse.Attr { 132 return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x 133 } 134 135 // Lookup performs a lookup under this node. 136 func (s *Root) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) { 137 log.Debugf("ipns: Root Lookup: '%s'", name) 138 switch name { 139 case "mach_kernel", ".hidden", "._.": 140 // Just quiet some log noise on OS X. 141 return nil, fuse.ENOENT 142 } 143 144 if name == "local" { 145 if s.LocalLink == nil { 146 return nil, fuse.ENOENT 147 } 148 return s.LocalLink, nil 149 } 150 151 nd, ok := s.LocalDirs[name] 152 if ok { 153 return nd, nil 154 } 155 156 log.Debugf("ipns: Falling back to resolution for [%s].", name) 157 resolved, err := s.Ipfs.Namesys.Resolve(name) 158 if err != nil { 159 log.Warningf("ipns: namesys resolve error: %s", err) 160 return nil, fuse.ENOENT 161 } 162 163 return &Link{s.IpfsRoot + "/" + resolved}, nil 164 } 165 166 // ReadDir reads a particular directory. Disallowed for root. 167 func (r *Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) { 168 log.Debug("Read Root.") 169 listing := []fuse.Dirent{ 170 fuse.Dirent{ 171 Name: "local", 172 Type: fuse.DT_Link, 173 }, 174 } 175 for _, k := range r.Keys { 176 pub := k.GetPublic() 177 hash, err := pub.Hash() 178 if err != nil { 179 log.Errorf("Read Root Error: %s", err) 180 continue 181 } 182 ent := fuse.Dirent{ 183 Name: u.Key(hash).Pretty(), 184 Type: fuse.DT_Dir, 185 } 186 listing = append(listing, ent) 187 } 188 return listing, nil 189 } 190 191 // Node is the core object representing a filesystem tree node. 192 type Node struct { 193 root *Root 194 nsRoot *Node 195 parent *Node 196 197 repub *Republisher 198 199 // This nodes name in its parent dir. 200 // NOTE: this strategy wont work well if we allow hard links 201 // (im all for murdering the thought of hard links) 202 name string 203 204 // Private keys held by nodes at the root of a keyspace 205 // WARNING(security): the PrivKey interface is currently insecure 206 // (holds the raw key). It will be secured later. 207 key ci.PrivKey 208 209 Ipfs *core.IpfsNode 210 Nd *mdag.Node 211 dagMod *uio.DagModifier 212 cached *ftpb.Data 213 } 214 215 func (s *Node) loadData() error { 216 s.cached = new(ftpb.Data) 217 return proto.Unmarshal(s.Nd.Data, s.cached) 218 } 219 220 // Attr returns the attributes of a given node. 221 func (s *Node) Attr() fuse.Attr { 222 if s.cached == nil { 223 err := s.loadData() 224 if err != nil { 225 log.Errorf("Error loading PBData for file: '%s'", s.name) 226 } 227 } 228 switch s.cached.GetType() { 229 case ftpb.Data_Directory: 230 return fuse.Attr{Mode: os.ModeDir | 0555} 231 case ftpb.Data_File, ftpb.Data_Raw: 232 size, err := ft.DataSize(s.Nd.Data) 233 if err != nil { 234 log.Errorf("Error getting size of file: %s", err) 235 size = 0 236 } 237 if size == 0 { 238 size = s.dagMod.Size() 239 } 240 return fuse.Attr{ 241 Mode: 0666, 242 Size: size, 243 Blocks: uint64(len(s.Nd.Links)), 244 } 245 default: 246 log.Error("Invalid data type.") 247 return fuse.Attr{} 248 } 249 } 250 251 // Lookup performs a lookup under this node. 252 func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) { 253 log.Debugf("ipns: node[%s] Lookup '%s'", s.name, name) 254 nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name}) 255 if err != nil { 256 // todo: make this error more versatile. 257 return nil, fuse.ENOENT 258 } 259 260 return s.makeChild(name, nd), nil 261 } 262 263 func (n *Node) makeChild(name string, node *mdag.Node) *Node { 264 child := &Node{ 265 Ipfs: n.Ipfs, 266 Nd: node, 267 name: name, 268 nsRoot: n.nsRoot, 269 parent: n, 270 } 271 272 // Always ensure that each child knows where the root is 273 if n.nsRoot == nil { 274 child.nsRoot = n 275 } else { 276 child.nsRoot = n.nsRoot 277 } 278 279 return child 280 } 281 282 // ReadDir reads the link structure as directory entries 283 func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) { 284 log.Debug("Node ReadDir") 285 entries := make([]fuse.Dirent, len(s.Nd.Links)) 286 for i, link := range s.Nd.Links { 287 n := link.Name 288 if len(n) == 0 { 289 n = link.Hash.B58String() 290 } 291 entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File} 292 } 293 294 if len(entries) > 0 { 295 return entries, nil 296 } 297 return nil, fuse.ENOENT 298 } 299 300 // ReadAll reads the object data as file data 301 func (s *Node) ReadAll(intr fs.Intr) ([]byte, fuse.Error) { 302 log.Debugf("ipns: ReadAll [%s]", s.name) 303 r, err := uio.NewDagReader(s.Nd, s.Ipfs.DAG) 304 if err != nil { 305 return nil, err 306 } 307 // this is a terrible function... 'ReadAll'? 308 // what if i have a 6TB file? GG RAM. 309 b, err := ioutil.ReadAll(r) 310 if err != nil { 311 log.Errorf("[%s] Readall error: %s", s.name, err) 312 return nil, err 313 } 314 return b, nil 315 } 316 317 func (n *Node) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error { 318 log.Debugf("ipns: Node Write [%s]: flags = %s, offset = %d, size = %d", n.name, req.Flags.String(), req.Offset, len(req.Data)) 319 320 if n.dagMod == nil { 321 // Create a DagModifier to allow us to change the existing dag node 322 dmod, err := uio.NewDagModifier(n.Nd, n.Ipfs.DAG, chunk.DefaultSplitter) 323 if err != nil { 324 log.Errorf("Error creating dag modifier: %s", err) 325 return err 326 } 327 n.dagMod = dmod 328 } 329 wrote, err := n.dagMod.WriteAt(req.Data, uint64(req.Offset)) 330 if err != nil { 331 return err 332 } 333 resp.Size = wrote 334 return nil 335 } 336 337 func (n *Node) Flush(req *fuse.FlushRequest, intr fs.Intr) fuse.Error { 338 log.Debugf("Got flush request [%s]!", n.name) 339 340 // If a write has happened 341 if n.dagMod != nil { 342 newNode, err := n.dagMod.GetNode() 343 if err != nil { 344 log.Errorf("Error getting dag node from dagMod: %s", err) 345 return err 346 } 347 348 if n.parent != nil { 349 log.Debug("updating self in parent!") 350 err := n.parent.update(n.name, newNode) 351 if err != nil { 352 log.Criticalf("error in updating ipns dag tree: %s", err) 353 // return fuse.ETHISISPRETTYBAD 354 return err 355 } 356 } 357 n.Nd = newNode 358 359 /*/TEMP 360 dr, err := mdag.NewDagReader(n.Nd, n.Ipfs.DAG) 361 if err != nil { 362 log.Critical("Verification read failed.") 363 } 364 b, err := ioutil.ReadAll(dr) 365 if err != nil { 366 log.Critical("Verification read failed.") 367 } 368 fmt.Println("VERIFICATION READ") 369 fmt.Printf("READ %d BYTES\n", len(b)) 370 fmt.Println(string(b)) 371 fmt.Println(b) 372 //*/ 373 374 n.dagMod = nil 375 376 n.wasChanged() 377 } 378 return nil 379 } 380 381 // Signal that a node in this tree was changed so the root can republish 382 func (n *Node) wasChanged() { 383 root := n.nsRoot 384 if root == nil { 385 root = n 386 } 387 388 root.repub.Publish <- struct{}{} 389 } 390 391 func (n *Node) republishRoot() error { 392 log.Debug("Republish root") 393 394 // We should already be the root, this is just a sanity check 395 var root *Node 396 if n.nsRoot != nil { 397 root = n.nsRoot 398 } else { 399 root = n 400 } 401 402 // Add any nodes that may be new to the DAG service 403 err := n.Ipfs.DAG.AddRecursive(root.Nd) 404 if err != nil { 405 log.Criticalf("ipns: Dag Add Error: %s", err) 406 return err 407 } 408 409 ndkey, err := root.Nd.Key() 410 if err != nil { 411 log.Errorf("getKey error: %s", err) 412 return err 413 } 414 log.Debug("Publishing changes!") 415 416 err = n.Ipfs.Namesys.Publish(root.key, ndkey.Pretty()) 417 if err != nil { 418 log.Errorf("ipns: Publish Failed: %s", err) 419 return err 420 } 421 return nil 422 } 423 424 func (n *Node) Fsync(req *fuse.FsyncRequest, intr fs.Intr) fuse.Error { 425 log.Debug("Got fsync request!") 426 return nil 427 } 428 429 func (n *Node) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) { 430 log.Debug("Got mkdir request!") 431 dagnd := &mdag.Node{Data: ft.FolderPBData()} 432 nnode := n.Nd.Copy() 433 nnode.AddNodeLink(req.Name, dagnd) 434 435 child := &Node{ 436 Ipfs: n.Ipfs, 437 Nd: dagnd, 438 name: req.Name, 439 } 440 441 if n.nsRoot == nil { 442 child.nsRoot = n 443 } else { 444 child.nsRoot = n.nsRoot 445 } 446 447 if n.parent != nil { 448 err := n.parent.update(n.name, nnode) 449 if err != nil { 450 log.Criticalf("Error updating node: %s", err) 451 return nil, err 452 } 453 } 454 n.Nd = nnode 455 456 n.wasChanged() 457 458 return child, nil 459 } 460 461 func (n *Node) Open(req *fuse.OpenRequest, resp *fuse.OpenResponse, intr fs.Intr) (fs.Handle, fuse.Error) { 462 //log.Debug("[%s] Received open request! flags = %s", n.name, req.Flags.String()) 463 //TODO: check open flags and truncate if necessary 464 if req.Flags&fuse.OpenTruncate != 0 { 465 log.Warning("Need to truncate file!") 466 n.cached = nil 467 n.Nd = &mdag.Node{Data: ft.FilePBData(nil, 0)} 468 } else if req.Flags&fuse.OpenAppend != 0 { 469 log.Warning("Need to append to file!") 470 } 471 return n, nil 472 } 473 474 func (n *Node) Mknod(req *fuse.MknodRequest, intr fs.Intr) (fs.Node, fuse.Error) { 475 log.Debug("Got mknod request!") 476 return nil, nil 477 } 478 479 func (n *Node) Create(req *fuse.CreateRequest, resp *fuse.CreateResponse, intr fs.Intr) (fs.Node, fs.Handle, fuse.Error) { 480 log.Debugf("Got create request: %s", req.Name) 481 482 // New 'empty' file 483 nd := &mdag.Node{Data: ft.FilePBData(nil, 0)} 484 child := n.makeChild(req.Name, nd) 485 486 nnode := n.Nd.Copy() 487 488 err := nnode.AddNodeLink(req.Name, nd) 489 if err != nil { 490 log.Errorf("Error adding child to node: %s", err) 491 return nil, nil, err 492 } 493 if n.parent != nil { 494 err := n.parent.update(n.name, nnode) 495 if err != nil { 496 log.Criticalf("Error updating node: %s", err) 497 // Can we panic, please? 498 return nil, nil, err 499 } 500 } 501 n.Nd = nnode 502 n.wasChanged() 503 504 return child, child, nil 505 } 506 507 func (n *Node) Remove(req *fuse.RemoveRequest, intr fs.Intr) fuse.Error { 508 log.Debugf("[%s] Got Remove request: %s", n.name, req.Name) 509 nnode := n.Nd.Copy() 510 err := nnode.RemoveNodeLink(req.Name) 511 if err != nil { 512 log.Error("Remove: No such file.") 513 return fuse.ENOENT 514 } 515 516 if n.parent != nil { 517 err := n.parent.update(n.name, nnode) 518 if err != nil { 519 log.Criticalf("Error updating node: %s", err) 520 return err 521 } 522 } 523 n.Nd = nnode 524 n.wasChanged() 525 return nil 526 } 527 528 func (n *Node) Rename(req *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fuse.Error { 529 log.Debugf("Got Rename request '%s' -> '%s'", req.OldName, req.NewName) 530 var mdn *mdag.Node 531 for _, l := range n.Nd.Links { 532 if l.Name == req.OldName { 533 mdn = l.Node 534 } 535 } 536 if mdn == nil { 537 log.Critical("nil Link found on rename!") 538 return fuse.ENOENT 539 } 540 n.Nd.RemoveNodeLink(req.OldName) 541 542 switch newDir := newDir.(type) { 543 case *Node: 544 err := newDir.Nd.AddNodeLink(req.NewName, mdn) 545 if err != nil { 546 log.Errorf("Error adding node to new dir on rename: %s", err) 547 return err 548 } 549 default: 550 log.Critical("Unknown node type for rename target dir!") 551 return errors.New("Unknown fs node type!") 552 } 553 return nil 554 } 555 556 // Updates the child of this node, specified by name to the given newnode 557 func (n *Node) update(name string, newnode *mdag.Node) error { 558 log.Debugf("update '%s' in '%s'", name, n.name) 559 nnode := n.Nd.Copy() 560 err := nnode.RemoveNodeLink(name) 561 if err != nil { 562 return err 563 } 564 nnode.AddNodeLink(name, newnode) 565 566 if n.parent != nil { 567 err := n.parent.update(n.name, nnode) 568 if err != nil { 569 return err 570 } 571 } 572 n.Nd = nnode 573 return nil 574 }