-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
various fixes for /ipfs fuse code #4194
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ import ( | |
|
||
logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" | ||
lgbl "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" | ||
format "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" | ||
proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" | ||
fuse "gx/ipfs/QmaFNtBAXX4nVMQWbUqNysXyhevUj1k4B1y5uS45LC7Vw9/fuse" | ||
fs "gx/ipfs/QmaFNtBAXX4nVMQWbUqNysXyhevUj1k4B1y5uS45LC7Vw9/fuse/fs" | ||
|
@@ -60,19 +61,26 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { | |
return nil, fuse.ENOENT | ||
} | ||
|
||
nd, err := s.Ipfs.Resolver.ResolvePath(ctx, path.Path(name)) | ||
p, err := path.ParsePath(name) | ||
if err != nil { | ||
log.Debugf("fuse failed to parse path: %q: %s", name, err) | ||
return nil, fuse.ENOENT | ||
} | ||
|
||
nd, err := s.Ipfs.Resolver.ResolvePath(ctx, p) | ||
if err != nil { | ||
// todo: make this error more versatile. | ||
return nil, fuse.ENOENT | ||
} | ||
|
||
pbnd, ok := nd.(*mdag.ProtoNode) | ||
if !ok { | ||
switch nd := nd.(type) { | ||
case *mdag.ProtoNode, *mdag.RawNode: | ||
return &Node{Ipfs: s.Ipfs, Nd: nd}, nil | ||
default: | ||
log.Error("fuse node was not a protobuf node") | ||
return nil, fuse.ENOTSUP | ||
} | ||
|
||
return &Node{Ipfs: s.Ipfs, Nd: pbnd}, nil | ||
} | ||
|
||
// ReadDirAll reads a particular directory. Disallowed for root. | ||
|
@@ -84,46 +92,48 @@ func (*Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { | |
// Node is the core object representing a filesystem tree node. | ||
type Node struct { | ||
Ipfs *core.IpfsNode | ||
Nd *mdag.ProtoNode | ||
Nd format.Node | ||
cached *ftpb.Data | ||
} | ||
|
||
func (s *Node) loadData() error { | ||
s.cached = new(ftpb.Data) | ||
return proto.Unmarshal(s.Nd.Data(), s.cached) | ||
if pbnd, ok := s.Nd.(*mdag.ProtoNode); ok { | ||
s.cached = new(ftpb.Data) | ||
return proto.Unmarshal(pbnd.Data(), s.cached) | ||
} | ||
return nil | ||
} | ||
|
||
// Attr returns the attributes of a given node. | ||
func (s *Node) Attr(ctx context.Context, a *fuse.Attr) error { | ||
log.Debug("Node attr") | ||
if rawnd, ok := s.Nd.(*mdag.RawNode); ok { | ||
a.Mode = 0444 | ||
a.Size = uint64(len(rawnd.RawData())) | ||
a.Blocks = 1 | ||
return nil | ||
} | ||
|
||
if s.cached == nil { | ||
if err := s.loadData(); err != nil { | ||
return fmt.Errorf("readonly: loadData() failed: %s", err) | ||
} | ||
} | ||
switch s.cached.GetType() { | ||
case ftpb.Data_Directory: | ||
case ftpb.Data_Directory, ftpb.Data_HAMTShard: | ||
a.Mode = os.ModeDir | 0555 | ||
a.Uid = uint32(os.Getuid()) | ||
a.Gid = uint32(os.Getgid()) | ||
case ftpb.Data_File: | ||
size := s.cached.GetFilesize() | ||
a.Mode = 0444 | ||
a.Size = uint64(size) | ||
a.Blocks = uint64(len(s.Nd.Links())) | ||
a.Uid = uint32(os.Getuid()) | ||
a.Gid = uint32(os.Getgid()) | ||
case ftpb.Data_Raw: | ||
a.Mode = 0444 | ||
a.Size = uint64(len(s.cached.GetData())) | ||
a.Blocks = uint64(len(s.Nd.Links())) | ||
a.Uid = uint32(os.Getuid()) | ||
a.Gid = uint32(os.Getgid()) | ||
case ftpb.Data_Symlink: | ||
a.Mode = 0777 | os.ModeSymlink | ||
a.Size = uint64(len(s.cached.GetData())) | ||
a.Uid = uint32(os.Getuid()) | ||
a.Gid = uint32(os.Getgid()) | ||
default: | ||
return fmt.Errorf("Invalid data type - %s", s.cached.GetType()) | ||
} | ||
|
@@ -133,31 +143,84 @@ func (s *Node) Attr(ctx context.Context, a *fuse.Attr) error { | |
// Lookup performs a lookup under this node. | ||
func (s *Node) Lookup(ctx context.Context, name string) (fs.Node, error) { | ||
log.Debugf("Lookup '%s'", name) | ||
nodes, err := s.Ipfs.Resolver.ResolveLinks(ctx, s.Nd, []string{name}) | ||
if err != nil { | ||
link, _, err := uio.ResolveUnixfsOnce(ctx, s.Ipfs.DAG, s.Nd, []string{name}) | ||
switch err { | ||
case os.ErrNotExist, mdag.ErrLinkNotFound: | ||
// todo: make this error more versatile. | ||
return nil, fuse.ENOENT | ||
default: | ||
log.Errorf("fuse lookup %q: %s", name, err) | ||
return nil, fuse.EIO | ||
case nil: | ||
// noop | ||
} | ||
|
||
pbnd, ok := nodes[len(nodes)-1].(*mdag.ProtoNode) | ||
if !ok { | ||
log.Error("fuse lookup got non-protobuf node") | ||
return nil, fuse.ENOTSUP | ||
nd, err := s.Ipfs.DAG.Get(ctx, link.Cid) | ||
switch err { | ||
case mdag.ErrNotFound: | ||
default: | ||
log.Errorf("fuse lookup %q: %s", name, err) | ||
return nil, err | ||
case nil: | ||
// noop | ||
} | ||
|
||
return &Node{Ipfs: s.Ipfs, Nd: pbnd}, nil | ||
return &Node{Ipfs: s.Ipfs, Nd: nd}, nil | ||
} | ||
|
||
// ReadDirAll reads the link structure as directory entries | ||
func (s *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { | ||
log.Debug("Node ReadDir") | ||
entries := make([]fuse.Dirent, len(s.Nd.Links())) | ||
for i, link := range s.Nd.Links() { | ||
n := link.Name | ||
dir, err := uio.NewDirectoryFromNode(s.Ipfs.DAG, s.Nd) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var entries []fuse.Dirent | ||
err = dir.ForEachLink(ctx, func(lnk *format.Link) error { | ||
n := lnk.Name | ||
if len(n) == 0 { | ||
n = link.Cid.String() | ||
n = lnk.Cid.String() | ||
} | ||
// TODO: calling everything a DT_File here might cause issues. But it | ||
// will be expensive to query each child. However most shells call an | ||
// additional 'stat' on each item in a directory listing, its probably | ||
// okay. | ||
nd, err := s.Ipfs.DAG.Get(ctx, lnk.Cid) | ||
if err != nil { | ||
log.Warning("error fetching directory child node: ", err) | ||
} | ||
|
||
var t fuse.DirentType | ||
switch nd := nd.(type) { | ||
case *mdag.RawNode: | ||
t = fuse.DT_File | ||
case *mdag.ProtoNode: | ||
var data ftpb.Data | ||
if err := proto.Unmarshal(nd.Data(), &data); err != nil { | ||
log.Warning("failed to unmarshal protonode data field:", err) | ||
} else { | ||
switch data.GetType() { | ||
case ftpb.Data_Directory, ftpb.Data_HAMTShard: | ||
t = fuse.DT_Dir | ||
case ftpb.Data_File, ftpb.Data_Raw: | ||
t = fuse.DT_File | ||
case ftpb.Data_Symlink: | ||
t = fuse.DT_Link | ||
case ftpb.Data_Metadata: | ||
log.Error("metadata object in fuse should contain its wrapped type") | ||
default: | ||
log.Error("unrecognized protonode data type: ", data.GetType()) | ||
} | ||
} | ||
default: | ||
t = fuse.DT_Unknown | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe put this up top (i.e., |
||
} | ||
entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File} | ||
entries = append(entries, fuse.Dirent{Name: n, Type: t}) | ||
return nil | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(entries) > 0 { | ||
|
@@ -166,15 +229,20 @@ func (s *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { | |
return nil, fuse.ENOENT | ||
} | ||
|
||
func (s *Node) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error { | ||
// TODO: is nil the right response for 'bug off, we aint got none' ? | ||
resp.Xattr = nil | ||
return nil | ||
} | ||
|
||
func (s *Node) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { | ||
if s.cached.GetType() != ftpb.Data_Symlink { | ||
if s.cached == nil || s.cached.GetType() != ftpb.Data_Symlink { | ||
return "", fuse.Errno(syscall.EINVAL) | ||
} | ||
return string(s.cached.GetData()), nil | ||
} | ||
|
||
func (s *Node) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { | ||
|
||
c := s.Nd.Cid() | ||
|
||
// setup our logging event | ||
|
@@ -220,6 +288,7 @@ type roNode interface { | |
fs.Node | ||
fs.NodeStringLookuper | ||
fs.NodeReadlinker | ||
fs.NodeGetxattrer | ||
} | ||
|
||
var _ roNode = (*Node)(nil) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -153,8 +153,13 @@ type sesGetter struct { | |
|
||
func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { | ||
blk, err := sg.bs.GetBlock(ctx, c) | ||
if err != nil { | ||
switch err { | ||
case bserv.ErrNotFound: | ||
return nil, ErrNotFound | ||
default: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're missing the case where |
||
return nil, err | ||
case nil: | ||
// noop | ||
} | ||
|
||
return node.Decode(blk) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer relevant.