Skip to content
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

store: Avoid access to Forgotten() before configured #1599

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions store/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"io"
"os/exec"
"sync"
"sync/atomic"
"syscall"
"time"

Expand Down Expand Up @@ -58,17 +59,21 @@ const (

func Mount(ctx context.Context, mountpoint string, layerManager *LayerManager, debug bool) error {
timeSec := time.Second
rawFS := fusefs.NewNodeFS(&rootnode{
fs: &fs{
layerManager: layerManager,
nodeMap: new(idMap),
layerMap: new(idMap),
},
}, &fusefs.Options{
AttrTimeout: &timeSec,
EntryTimeout: &timeSec,
NullPermissions: true,
})
fs := &fs{
layerManager: layerManager,
nodeMap: new(idMap),
layerMap: new(idMap),
}
rawFS := &fsLocker{
RawFileSystem: fusefs.NewNodeFS(&rootnode{
fs: fs,
}, &fusefs.Options{
AttrTimeout: &timeSec,
EntryTimeout: &timeSec,
NullPermissions: true,
}),
}
fs.fsLocker = rawFS.wLocker()
mountOpts := &fuse.MountOptions{
AllowOther: true, // allow users other than root&mounter to access fs
FsName: "stargzstore",
Expand All @@ -88,6 +93,21 @@ func Mount(ctx context.Context, mountpoint string, layerManager *LayerManager, d
return server.WaitMount()
}

type fsLocker struct {
fuse.RawFileSystem
mu sync.RWMutex
}

func (fs *fsLocker) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name string, out *fuse.EntryOut) (status fuse.Status) {
fs.mu.RLock()
defer fs.mu.RUnlock()
return fs.RawFileSystem.Lookup(cancel, header, name, out)
}

func (fs *fsLocker) wLocker() sync.Locker {
return &(fs.mu)
}

type fs struct {
layerManager *LayerManager

Expand All @@ -103,6 +123,8 @@ type fs struct {

knownNode map[string]map[string]*layerReleasable
knownNodeMu sync.Mutex

fsLocker sync.Locker
}

type layerReleasable struct {
Expand Down Expand Up @@ -137,18 +159,34 @@ func isForgotten(n *fusefs.Inode) bool {
}

type inoReleasable struct {
n fusefs.InodeEmbedder
n fusefs.InodeEmbedder
fsLocker sync.Locker

g singleflight.Group
isReleasable atomic.Bool
}

func (r *inoReleasable) releasable() bool {
return r.n.EmbeddedInode().Forgotten()
go r.g.Do("releasable", func() (interface{}, error) {
// Forgotten accesses the internal variables that are initialized during call
// to Lookup API. We want to avoid accessing these variables before they're fully
// initialized so we use a lock.
r.fsLocker.Lock()
forgotten := r.n.EmbeddedInode().Forgotten()
r.fsLocker.Unlock()
if forgotten {
r.isReleasable.Store(true)
}
return nil, nil
})
return r.isReleasable.Load()
}

func (fs *fs) newInodeWithID(ctx context.Context, p func(uint32) fusefs.InodeEmbedder) (*fusefs.Inode, syscall.Errno) {
var ino fusefs.InodeEmbedder
if err := fs.nodeMap.add(func(id uint32) (releasable, error) {
ino = p(id)
return &inoReleasable{ino}, nil
return &inoReleasable{n: ino, fsLocker: fs.fsLocker}, nil
}); err != nil || ino == nil {
log.G(ctx).WithError(err).Debug("cannot generate ID")
return nil, syscall.EIO
Expand Down
Loading