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 }