github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/fuse/ipns/ipns_unix.go (about) 1 // +build !nofuse 2 3 // package fuse/ipns implements a fuse filesystem that interfaces 4 // with ipns, the naming system for ipfs. 5 package ipns 6 7 import ( 8 "errors" 9 "fmt" 10 "os" 11 "strings" 12 13 fuse "github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse" 14 fs "github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs" 15 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 16 eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" 17 18 key "github.com/ipfs/go-ipfs/blocks/key" 19 core "github.com/ipfs/go-ipfs/core" 20 nsfs "github.com/ipfs/go-ipfs/ipnsfs" 21 dag "github.com/ipfs/go-ipfs/merkledag" 22 ci "github.com/ipfs/go-ipfs/p2p/crypto" 23 ft "github.com/ipfs/go-ipfs/unixfs" 24 ) 25 26 var log = eventlog.Logger("fuse/ipns") 27 28 // FileSystem is the readwrite IPNS Fuse Filesystem. 29 type FileSystem struct { 30 Ipfs *core.IpfsNode 31 RootNode *Root 32 } 33 34 // NewFileSystem constructs new fs using given core.IpfsNode instance. 35 func NewFileSystem(ipfs *core.IpfsNode, sk ci.PrivKey, ipfspath, ipnspath string) (*FileSystem, error) { 36 root, err := CreateRoot(ipfs, []ci.PrivKey{sk}, ipfspath, ipnspath) 37 if err != nil { 38 return nil, err 39 } 40 return &FileSystem{Ipfs: ipfs, RootNode: root}, nil 41 } 42 43 // Root constructs the Root of the filesystem, a Root object. 44 func (f *FileSystem) Root() (fs.Node, error) { 45 log.Debug("Filesystem, get root") 46 return f.RootNode, nil 47 } 48 49 func (f *FileSystem) Destroy() { 50 err := f.RootNode.Close() 51 if err != nil { 52 log.Errorf("Error Shutting Down Filesystem: %s\n", err) 53 } 54 } 55 56 // Root is the root object of the filesystem tree. 57 type Root struct { 58 Ipfs *core.IpfsNode 59 Keys []ci.PrivKey 60 61 // Used for symlinking into ipfs 62 IpfsRoot string 63 IpnsRoot string 64 LocalDirs map[string]fs.Node 65 Roots map[string]*nsfs.KeyRoot 66 67 fs *nsfs.Filesystem 68 LocalLink *Link 69 } 70 71 func CreateRoot(ipfs *core.IpfsNode, keys []ci.PrivKey, ipfspath, ipnspath string) (*Root, error) { 72 ldirs := make(map[string]fs.Node) 73 roots := make(map[string]*nsfs.KeyRoot) 74 for _, k := range keys { 75 pkh, err := k.GetPublic().Hash() 76 if err != nil { 77 return nil, err 78 } 79 name := key.Key(pkh).B58String() 80 root, err := ipfs.IpnsFs.GetRoot(name) 81 if err != nil { 82 return nil, err 83 } 84 85 roots[name] = root 86 87 switch val := root.GetValue().(type) { 88 case *nsfs.Directory: 89 ldirs[name] = &Directory{dir: val} 90 case *nsfs.File: 91 ldirs[name] = &File{fi: val} 92 default: 93 return nil, errors.New("unrecognized type") 94 } 95 } 96 97 return &Root{ 98 fs: ipfs.IpnsFs, 99 Ipfs: ipfs, 100 IpfsRoot: ipfspath, 101 IpnsRoot: ipnspath, 102 Keys: keys, 103 LocalDirs: ldirs, 104 LocalLink: &Link{ipfs.Identity.Pretty()}, 105 Roots: roots, 106 }, nil 107 } 108 109 // Attr returns file attributes. 110 func (*Root) Attr(ctx context.Context, a *fuse.Attr) error { 111 log.Debug("Root Attr") 112 *a = fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x 113 return nil 114 } 115 116 // Lookup performs a lookup under this node. 117 func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { 118 switch name { 119 case "mach_kernel", ".hidden", "._.": 120 // Just quiet some log noise on OS X. 121 return nil, fuse.ENOENT 122 } 123 124 // Local symlink to the node ID keyspace 125 if name == "local" { 126 if s.LocalLink == nil { 127 return nil, fuse.ENOENT 128 } 129 return s.LocalLink, nil 130 } 131 132 nd, ok := s.LocalDirs[name] 133 if ok { 134 switch nd := nd.(type) { 135 case *Directory: 136 return nd, nil 137 case *File: 138 return nd, nil 139 default: 140 return nil, fuse.EIO 141 } 142 } 143 144 // other links go through ipns resolution and are symlinked into the ipfs mountpoint 145 resolved, err := s.Ipfs.Namesys.Resolve(s.Ipfs.Context(), name) 146 if err != nil { 147 log.Warningf("ipns: namesys resolve error: %s", err) 148 return nil, fuse.ENOENT 149 } 150 151 segments := resolved.Segments() 152 if segments[0] == "ipfs" { 153 p := strings.Join(resolved.Segments()[1:], "/") 154 return &Link{s.IpfsRoot + "/" + p}, nil 155 } else { 156 log.Error("Invalid path.Path: ", resolved) 157 return nil, errors.New("invalid path from ipns record") 158 } 159 } 160 161 func (r *Root) Close() error { 162 for _, kr := range r.Roots { 163 err := kr.Publish(r.Ipfs.Context()) 164 if err != nil { 165 return err 166 } 167 } 168 return nil 169 } 170 171 // Forget is called when the filesystem is unmounted. probably. 172 // see comments here: http://godoc.org/bazil.org/fuse/fs#FSDestroyer 173 func (r *Root) Forget() { 174 err := r.Close() 175 if err != nil { 176 log.Error(err) 177 } 178 } 179 180 // ReadDirAll reads a particular directory. Will show locally available keys 181 // as well as a symlink to the peerID key 182 func (r *Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { 183 log.Debug("Root ReadDirAll") 184 listing := []fuse.Dirent{ 185 { 186 Name: "local", 187 Type: fuse.DT_Link, 188 }, 189 } 190 for _, k := range r.Keys { 191 pub := k.GetPublic() 192 hash, err := pub.Hash() 193 if err != nil { 194 continue 195 } 196 ent := fuse.Dirent{ 197 Name: key.Key(hash).Pretty(), 198 Type: fuse.DT_Dir, 199 } 200 listing = append(listing, ent) 201 } 202 return listing, nil 203 } 204 205 // Directory is wrapper over an ipnsfs directory to satisfy the fuse fs interface 206 type Directory struct { 207 dir *nsfs.Directory 208 209 fs.NodeRef 210 } 211 212 // File is wrapper over an ipnsfs file to satisfy the fuse fs interface 213 type File struct { 214 fi *nsfs.File 215 216 fs.NodeRef 217 } 218 219 // Attr returns the attributes of a given node. 220 func (d *Directory) Attr(ctx context.Context, a *fuse.Attr) error { 221 log.Debug("Directory Attr") 222 *a = fuse.Attr{ 223 Mode: os.ModeDir | 0555, 224 Uid: uint32(os.Getuid()), 225 Gid: uint32(os.Getgid()), 226 } 227 return nil 228 } 229 230 // Attr returns the attributes of a given node. 231 func (fi *File) Attr(ctx context.Context, a *fuse.Attr) error { 232 log.Debug("File Attr") 233 size, err := fi.fi.Size() 234 if err != nil { 235 // In this case, the dag node in question may not be unixfs 236 return fmt.Errorf("fuse/ipns: failed to get file.Size(): %s", err) 237 } 238 *a = fuse.Attr{ 239 Mode: os.FileMode(0666), 240 Size: uint64(size), 241 Uid: uint32(os.Getuid()), 242 Gid: uint32(os.Getgid()), 243 } 244 return nil 245 } 246 247 // Lookup performs a lookup under this node. 248 func (s *Directory) Lookup(ctx context.Context, name string) (fs.Node, error) { 249 child, err := s.dir.Child(name) 250 if err != nil { 251 // todo: make this error more versatile. 252 return nil, fuse.ENOENT 253 } 254 255 switch child := child.(type) { 256 case *nsfs.Directory: 257 return &Directory{dir: child}, nil 258 case *nsfs.File: 259 return &File{fi: child}, nil 260 default: 261 // NB: if this happens, we do not want to continue, unpredictable behaviour 262 // may occur. 263 panic("invalid type found under directory. programmer error.") 264 } 265 } 266 267 // ReadDirAll reads the link structure as directory entries 268 func (dir *Directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { 269 var entries []fuse.Dirent 270 for _, name := range dir.dir.List() { 271 dirent := fuse.Dirent{Name: name} 272 273 // TODO: make dir.dir.List() return dirinfos 274 child, err := dir.dir.Child(name) 275 if err != nil { 276 return nil, err 277 } 278 279 switch child.Type() { 280 case nsfs.TDir: 281 dirent.Type = fuse.DT_Dir 282 case nsfs.TFile: 283 dirent.Type = fuse.DT_File 284 } 285 286 entries = append(entries, dirent) 287 } 288 289 if len(entries) > 0 { 290 return entries, nil 291 } 292 return nil, fuse.ENOENT 293 } 294 295 func (fi *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { 296 _, err := fi.fi.Seek(req.Offset, os.SEEK_SET) 297 if err != nil { 298 return err 299 } 300 301 fisize, err := fi.fi.Size() 302 if err != nil { 303 return err 304 } 305 306 select { 307 case <-ctx.Done(): 308 return ctx.Err() 309 default: 310 } 311 312 readsize := min(req.Size, int(fisize-req.Offset)) 313 n, err := fi.fi.CtxReadFull(ctx, resp.Data[:readsize]) 314 resp.Data = resp.Data[:n] 315 return err 316 } 317 318 func (fi *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { 319 // TODO: at some point, ensure that WriteAt here respects the context 320 wrote, err := fi.fi.WriteAt(req.Data, req.Offset) 321 if err != nil { 322 return err 323 } 324 resp.Size = wrote 325 return nil 326 } 327 328 func (fi *File) Flush(ctx context.Context, req *fuse.FlushRequest) error { 329 errs := make(chan error, 1) 330 go func() { 331 errs <- fi.fi.Close() 332 }() 333 select { 334 case err := <-errs: 335 return err 336 case <-ctx.Done(): 337 return ctx.Err() 338 } 339 } 340 341 func (fi *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error { 342 cursize, err := fi.fi.Size() 343 if err != nil { 344 return err 345 } 346 if cursize != int64(req.Size) { 347 err := fi.fi.Truncate(int64(req.Size)) 348 if err != nil { 349 return err 350 } 351 } 352 return nil 353 } 354 355 // Fsync flushes the content in the file to disk, but does not 356 // update the dag tree internally 357 func (fi *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error { 358 errs := make(chan error, 1) 359 go func() { 360 errs <- fi.fi.Sync() 361 }() 362 select { 363 case err := <-errs: 364 return err 365 case <-ctx.Done(): 366 return ctx.Err() 367 } 368 } 369 370 func (fi *File) Forget() { 371 err := fi.fi.Sync() 372 if err != nil { 373 log.Debug("Forget file error: ", err) 374 } 375 } 376 377 func (dir *Directory) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { 378 child, err := dir.dir.Mkdir(req.Name) 379 if err != nil { 380 return nil, err 381 } 382 383 return &Directory{dir: child}, nil 384 } 385 386 func (fi *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { 387 if req.Flags&fuse.OpenTruncate != 0 { 388 log.Info("Need to truncate file!") 389 err := fi.fi.Truncate(0) 390 if err != nil { 391 return nil, err 392 } 393 } else if req.Flags&fuse.OpenAppend != 0 { 394 log.Info("Need to append to file!") 395 396 // seek(0) essentially resets the file object, this is required for appends to work 397 // properly 398 _, err := fi.fi.Seek(0, os.SEEK_SET) 399 if err != nil { 400 log.Error("seek reset failed: ", err) 401 return nil, err 402 } 403 } 404 return fi, nil 405 } 406 407 func (fi *File) Release(ctx context.Context, req *fuse.ReleaseRequest) error { 408 return fi.fi.Close() 409 } 410 411 func (dir *Directory) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { 412 // New 'empty' file 413 nd := &dag.Node{Data: ft.FilePBData(nil, 0)} 414 err := dir.dir.AddChild(req.Name, nd) 415 if err != nil { 416 return nil, nil, err 417 } 418 419 child, err := dir.dir.Child(req.Name) 420 if err != nil { 421 return nil, nil, err 422 } 423 424 fi, ok := child.(*nsfs.File) 425 if !ok { 426 return nil, nil, errors.New("child creation failed") 427 } 428 429 nodechild := &File{fi: fi} 430 return nodechild, nodechild, nil 431 } 432 433 func (dir *Directory) Remove(ctx context.Context, req *fuse.RemoveRequest) error { 434 err := dir.dir.Unlink(req.Name) 435 if err != nil { 436 return fuse.ENOENT 437 } 438 return nil 439 } 440 441 // Rename implements NodeRenamer 442 func (dir *Directory) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error { 443 cur, err := dir.dir.Child(req.OldName) 444 if err != nil { 445 return err 446 } 447 448 err = dir.dir.Unlink(req.OldName) 449 if err != nil { 450 return err 451 } 452 453 switch newDir := newDir.(type) { 454 case *Directory: 455 nd, err := cur.GetNode() 456 if err != nil { 457 return err 458 } 459 460 err = newDir.dir.AddChild(req.NewName, nd) 461 if err != nil { 462 return err 463 } 464 case *File: 465 log.Error("Cannot move node into a file!") 466 return fuse.EPERM 467 default: 468 log.Error("Unknown node type for rename target dir!") 469 return errors.New("Unknown fs node type!") 470 } 471 return nil 472 } 473 474 func min(a, b int) int { 475 if a < b { 476 return a 477 } 478 return b 479 } 480 481 // to check that out Node implements all the interfaces we want 482 type ipnsRoot interface { 483 fs.Node 484 fs.HandleReadDirAller 485 fs.NodeStringLookuper 486 } 487 488 var _ ipnsRoot = (*Root)(nil) 489 490 type ipnsDirectory interface { 491 fs.HandleReadDirAller 492 fs.Node 493 fs.NodeCreater 494 fs.NodeMkdirer 495 fs.NodeRemover 496 fs.NodeRenamer 497 fs.NodeStringLookuper 498 } 499 500 var _ ipnsDirectory = (*Directory)(nil) 501 502 type ipnsFile interface { 503 fs.HandleFlusher 504 fs.HandleReader 505 fs.HandleWriter 506 fs.HandleReleaser 507 fs.Node 508 fs.NodeFsyncer 509 fs.NodeOpener 510 } 511 512 var _ ipnsFile = (*File)(nil)