github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/fuse/readonly/readonly_unix.go (about)

     1  // +build linux darwin freebsd
     2  // +build !nofuse
     3  
     4  package readonly
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"syscall"
    11  
    12  	fuse "github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
    13  	fs "github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
    14  	proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
    15  	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    16  	core "github.com/ipfs/go-ipfs/core"
    17  	mdag "github.com/ipfs/go-ipfs/merkledag"
    18  	path "github.com/ipfs/go-ipfs/path"
    19  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    20  	uio "github.com/ipfs/go-ipfs/unixfs/io"
    21  	ftpb "github.com/ipfs/go-ipfs/unixfs/pb"
    22  	lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables"
    23  )
    24  
    25  var log = eventlog.Logger("fuse/ipfs")
    26  
    27  // FileSystem is the readonly Ipfs Fuse Filesystem.
    28  type FileSystem struct {
    29  	Ipfs *core.IpfsNode
    30  }
    31  
    32  // NewFileSystem constructs new fs using given core.IpfsNode instance.
    33  func NewFileSystem(ipfs *core.IpfsNode) *FileSystem {
    34  	return &FileSystem{Ipfs: ipfs}
    35  }
    36  
    37  // Root constructs the Root of the filesystem, a Root object.
    38  func (f FileSystem) Root() (fs.Node, error) {
    39  	return &Root{Ipfs: f.Ipfs}, nil
    40  }
    41  
    42  // Root is the root object of the filesystem tree.
    43  type Root struct {
    44  	Ipfs *core.IpfsNode
    45  }
    46  
    47  // Attr returns file attributes.
    48  func (*Root) Attr(ctx context.Context, a *fuse.Attr) error {
    49  	*a = fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x
    50  	return nil
    51  }
    52  
    53  // Lookup performs a lookup under this node.
    54  func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
    55  	log.Debugf("Root Lookup: '%s'", name)
    56  	switch name {
    57  	case "mach_kernel", ".hidden", "._.":
    58  		// Just quiet some log noise on OS X.
    59  		return nil, fuse.ENOENT
    60  	}
    61  
    62  	nd, err := s.Ipfs.Resolver.ResolvePath(ctx, path.Path(name))
    63  	if err != nil {
    64  		// todo: make this error more versatile.
    65  		return nil, fuse.ENOENT
    66  	}
    67  
    68  	return &Node{Ipfs: s.Ipfs, Nd: nd}, nil
    69  }
    70  
    71  // ReadDirAll reads a particular directory. Disallowed for root.
    72  func (*Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
    73  	log.Debug("Read Root.")
    74  	return nil, fuse.EPERM
    75  }
    76  
    77  // Node is the core object representing a filesystem tree node.
    78  type Node struct {
    79  	Ipfs   *core.IpfsNode
    80  	Nd     *mdag.Node
    81  	fd     *uio.DagReader
    82  	cached *ftpb.Data
    83  }
    84  
    85  func (s *Node) loadData() error {
    86  	s.cached = new(ftpb.Data)
    87  	return proto.Unmarshal(s.Nd.Data, s.cached)
    88  }
    89  
    90  // Attr returns the attributes of a given node.
    91  func (s *Node) Attr(ctx context.Context, a *fuse.Attr) error {
    92  	log.Debug("Node attr.")
    93  	if s.cached == nil {
    94  		if err := s.loadData(); err != nil {
    95  			return fmt.Errorf("readonly: loadData() failed: %s", err)
    96  		}
    97  	}
    98  	switch s.cached.GetType() {
    99  	case ftpb.Data_Directory:
   100  		a.Mode = os.ModeDir | 0555
   101  		a.Uid = uint32(os.Getuid())
   102  		a.Gid = uint32(os.Getgid())
   103  	case ftpb.Data_File:
   104  		size := s.cached.GetFilesize()
   105  		a.Mode = 0444
   106  		a.Size = uint64(size)
   107  		a.Blocks = uint64(len(s.Nd.Links))
   108  		a.Uid = uint32(os.Getuid())
   109  		a.Gid = uint32(os.Getgid())
   110  	case ftpb.Data_Raw:
   111  		a.Mode = 0444
   112  		a.Size = uint64(len(s.cached.GetData()))
   113  		a.Blocks = uint64(len(s.Nd.Links))
   114  		a.Uid = uint32(os.Getuid())
   115  		a.Gid = uint32(os.Getgid())
   116  	case ftpb.Data_Symlink:
   117  		a.Mode = 0777 | os.ModeSymlink
   118  		a.Size = uint64(len(s.cached.GetData()))
   119  		a.Uid = uint32(os.Getuid())
   120  		a.Gid = uint32(os.Getgid())
   121  
   122  	default:
   123  		return fmt.Errorf("Invalid data type - %s", s.cached.GetType())
   124  	}
   125  	return nil
   126  }
   127  
   128  // Lookup performs a lookup under this node.
   129  func (s *Node) Lookup(ctx context.Context, name string) (fs.Node, error) {
   130  	log.Debugf("Lookup '%s'", name)
   131  	nodes, err := s.Ipfs.Resolver.ResolveLinks(ctx, s.Nd, []string{name})
   132  	if err != nil {
   133  		// todo: make this error more versatile.
   134  		return nil, fuse.ENOENT
   135  	}
   136  
   137  	return &Node{Ipfs: s.Ipfs, Nd: nodes[len(nodes)-1]}, nil
   138  }
   139  
   140  // ReadDirAll reads the link structure as directory entries
   141  func (s *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
   142  	log.Debug("Node ReadDir")
   143  	entries := make([]fuse.Dirent, len(s.Nd.Links))
   144  	for i, link := range s.Nd.Links {
   145  		n := link.Name
   146  		if len(n) == 0 {
   147  			n = link.Hash.B58String()
   148  		}
   149  		entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File}
   150  	}
   151  
   152  	if len(entries) > 0 {
   153  		return entries, nil
   154  	}
   155  	return nil, fuse.ENOENT
   156  }
   157  
   158  func (s *Node) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
   159  	if s.cached.GetType() != ftpb.Data_Symlink {
   160  		return "", fuse.Errno(syscall.EINVAL)
   161  	}
   162  	return string(s.cached.GetData()), nil
   163  }
   164  
   165  func (s *Node) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
   166  
   167  	k, err := s.Nd.Key()
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	// setup our logging event
   173  	lm := make(lgbl.DeferredMap)
   174  	lm["fs"] = "ipfs"
   175  	lm["key"] = func() interface{} { return k.Pretty() }
   176  	lm["req_offset"] = req.Offset
   177  	lm["req_size"] = req.Size
   178  	defer log.EventBegin(ctx, "fuseRead", lm).Done()
   179  
   180  	r, err := uio.NewDagReader(ctx, s.Nd, s.Ipfs.DAG)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	o, err := r.Seek(req.Offset, os.SEEK_SET)
   185  	lm["res_offset"] = o
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	buf := resp.Data[:min(req.Size, int(int64(r.Size())-req.Offset))]
   191  	n, err := io.ReadFull(r, buf)
   192  	if err != nil && err != io.EOF {
   193  		return err
   194  	}
   195  	resp.Data = resp.Data[:n]
   196  	lm["res_size"] = n
   197  	return nil // may be non-nil / not succeeded
   198  }
   199  
   200  // to check that out Node implements all the interfaces we want
   201  type roRoot interface {
   202  	fs.Node
   203  	fs.HandleReadDirAller
   204  	fs.NodeStringLookuper
   205  }
   206  
   207  var _ roRoot = (*Root)(nil)
   208  
   209  type roNode interface {
   210  	fs.HandleReadDirAller
   211  	fs.HandleReader
   212  	fs.Node
   213  	fs.NodeStringLookuper
   214  	fs.NodeReadlinker
   215  }
   216  
   217  var _ roNode = (*Node)(nil)
   218  
   219  func min(a, b int) int {
   220  	if a < b {
   221  		return a
   222  	}
   223  	return b
   224  }