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
Provide same Node instance for same file not to make overlayfs confused. #19
Conversation
Currently, CRFS returns different "Node" instances everytime "Lookup" is called. This behaviour makes bazil/fuse assign different "Node ID"(used by FUSE) to inodes everytime even if these inodes are describing same file.(The reason is that bazil/fuse caches "Node IDs" keyed by a "Node" insatance(not an inode number etc.)) Most time(when we don't use overlayfs etc.) it is fine, but overlayfs doesn't allow this behaviour when it revalidates dentry cache, so returns ESTALE. As a result, CRFS currently doesn't support to merge the layers using overlayfs. This commit solves this issue and make CRFS support overlayfs by cache node instances in CRFS once it lookedup and useing it when the same name is looked up. Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
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.
Thanks!
crfs.go
Outdated
@@ -901,6 +903,7 @@ type node struct { | |||
te *stargz.TOCEntry | |||
sr *stargz.Reader | |||
f *os.File // non-nil if root & in debug mode | |||
children map[string]fspkg.Node // Remenber child nodes once looked up. |
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.
typo: remember
But more important is to document what the keys look like. It might make sense to use a before-line comment and spell it all out:
// children maps from previously-looked up base names (like "foo.txt") to
// the *node that was previously returned. This prevents FUSE inode numbers
// from getting out of sync
child map[string]*node
Also, I'd make the value type just be *node
. It's slightly faster and smaller, but it's also more clear because that's the only type we'll have there, right?
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.
Thank you for your review. I fixed it on b076f18.
crfs.go
Outdated
e, ok := n.te.LookupChild(name) | ||
if !ok { | ||
return nil, fuse.ENOENT | ||
} | ||
return &node{n.fs, e, n.sr, nil}, nil | ||
|
||
c := &node{n.fs, e, n.sr, nil, make(map[string]fspkg.Node)} |
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.
Arguably positional initializing was already overdue before, but now it really is. Switch these to named fields here and drop the nil entry.
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.
Thank you for your review. I fixed it on b076f18.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
crfs.go
Outdated
@@ -966,11 +973,24 @@ func (h *nodeHandle) ReadDirAll(ctx context.Context) (ents []fuse.Dirent, err er | |||
// | |||
// See https://godoc.org/bazil.org/fuse/fs#NodeStringLookuper | |||
func (n *node) Lookup(ctx context.Context, name string) (fspkg.Node, error) { | |||
if c, ok := n.child[name] ; ok { |
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.
This looks like a data race. The node.Lookup can be called from concurrent goroutines and no mutex is protecting this map read/write.
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.
Right. I added mutex to protect children maps in e18ee07.
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.
That's not enough. This read is also a race.
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.
Thank you for your review. I fixed it on 39ae89f.
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.
Run gofmt. No space before ;.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
@bradfitz Could I get any comments? |
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
crfs.go
Outdated
@@ -901,6 +903,12 @@ type node struct { | |||
te *stargz.TOCEntry | |||
sr *stargz.Reader | |||
f *os.File // non-nil if root & in debug mode | |||
|
|||
mu sync.Mutex // For children maps. |
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.
mu sync.Mutex // guards child, below
crfs.go
Outdated
@@ -901,6 +903,12 @@ type node struct { | |||
te *stargz.TOCEntry | |||
sr *stargz.Reader | |||
f *os.File // non-nil if root & in debug mode | |||
|
|||
mu sync.Mutex // For children maps. | |||
// children maps from previously-looked up base names (like "foo.txt") to |
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.
// child maps ...
(The field is named "child", not "children")
crfs.go
Outdated
@@ -966,11 +973,24 @@ func (h *nodeHandle) ReadDirAll(ctx context.Context) (ents []fuse.Dirent, err er | |||
// | |||
// See https://godoc.org/bazil.org/fuse/fs#NodeStringLookuper | |||
func (n *node) Lookup(ctx context.Context, name string) (fspkg.Node, error) { | |||
if c, ok := n.child[name] ; ok { |
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.
Run gofmt. No space before ;.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
@bradfitz Could I get any comments? |
Fixes: #16
CRFS currently doesn't support to merge the layers using overlayfs.
CRFS generates different "Node" instance every time "Lookup" is called. This behaviour makes bazil/fuse assign different "Node IDs"(used by FUSE) to inodes every time even if these "Lookups" point to same file because bazil/fuse caches "Node IDs" keyed by a "Node" instance (not an inode number etc.). Most time (when we don't use overlayfs etc.) it is fine.
However, when dentry cache revalidation is executed and the dentry is expired (by default, set to 1 min in bazil/fuse.), FUSE "lookups" the original inode and it doesn't allow different Node IDs to same inode and concludes the cache as invalid. Unfortunately, overlayfs doesn't allow dentry caches being invalid and returns ESTALE.
This commit solves this issue and make CRFS support overlayfs by cache node
instances in CRFS once it looked-up and using it when the same name is looked
up.