Avoid holding locks when opening files in VFS2.
Fixes #3243, #3521 PiperOrigin-RevId: 327308890
This commit is contained in:
parent
760c131da1
commit
6405525b04
|
@ -834,7 +834,14 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
|||
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
unlocked := false
|
||||
unlock := func() {
|
||||
if !unlocked {
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
unlocked = true
|
||||
}
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
start := rp.Start().Impl().(*dentry)
|
||||
if !start.cachedMetadataAuthoritative() {
|
||||
|
@ -851,7 +858,10 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
|||
if mustCreate {
|
||||
return nil, syserror.EEXIST
|
||||
}
|
||||
return start.openLocked(ctx, rp, &opts)
|
||||
start.IncRef()
|
||||
defer start.DecRef(ctx)
|
||||
unlock()
|
||||
return start.open(ctx, rp, &opts)
|
||||
}
|
||||
|
||||
afterTrailingSymlink:
|
||||
|
@ -901,11 +911,15 @@ afterTrailingSymlink:
|
|||
if rp.MustBeDir() && !child.isDir() {
|
||||
return nil, syserror.ENOTDIR
|
||||
}
|
||||
return child.openLocked(ctx, rp, &opts)
|
||||
child.IncRef()
|
||||
defer child.DecRef(ctx)
|
||||
unlock()
|
||||
return child.open(ctx, rp, &opts)
|
||||
}
|
||||
|
||||
// Preconditions: fs.renameMu must be locked.
|
||||
func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
// Preconditions: The caller must hold no locks (since opening pipes may block
|
||||
// indefinitely).
|
||||
func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
ats := vfs.AccessTypesForOpenFlags(opts)
|
||||
if err := d.checkPermissions(rp.Credentials(), ats); err != nil {
|
||||
return nil, err
|
||||
|
@ -968,7 +982,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
|
|||
return nil, syserror.ENXIO
|
||||
}
|
||||
if d.fs.iopts.OpenSocketsByConnecting {
|
||||
return d.connectSocketLocked(ctx, opts)
|
||||
return d.openSocketByConnecting(ctx, opts)
|
||||
}
|
||||
case linux.S_IFIFO:
|
||||
if d.isSynthetic() {
|
||||
|
@ -977,7 +991,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
|
|||
}
|
||||
|
||||
if vfd == nil {
|
||||
if vfd, err = d.openSpecialFileLocked(ctx, mnt, opts); err != nil {
|
||||
if vfd, err = d.openSpecialFile(ctx, mnt, opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -996,7 +1010,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
|
|||
return vfd, err
|
||||
}
|
||||
|
||||
func (d *dentry) connectSocketLocked(ctx context.Context, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
func (d *dentry) openSocketByConnecting(ctx context.Context, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
if opts.Flags&linux.O_DIRECT != 0 {
|
||||
return nil, syserror.EINVAL
|
||||
}
|
||||
|
@ -1016,7 +1030,7 @@ func (d *dentry) connectSocketLocked(ctx context.Context, opts *vfs.OpenOptions)
|
|||
return fd, nil
|
||||
}
|
||||
|
||||
func (d *dentry) openSpecialFileLocked(ctx context.Context, mnt *vfs.Mount, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
func (d *dentry) openSpecialFile(ctx context.Context, mnt *vfs.Mount, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
ats := vfs.AccessTypesForOpenFlags(opts)
|
||||
if opts.Flags&linux.O_DIRECT != 0 {
|
||||
return nil, syserror.EINVAL
|
||||
|
|
|
@ -397,15 +397,21 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
|||
// Do not create new file.
|
||||
if opts.Flags&linux.O_CREAT == 0 {
|
||||
fs.mu.RLock()
|
||||
defer fs.processDeferredDecRefs(ctx)
|
||||
defer fs.mu.RUnlock()
|
||||
vfsd, inode, err := fs.walkExistingLocked(ctx, rp)
|
||||
if err != nil {
|
||||
fs.mu.RUnlock()
|
||||
fs.processDeferredDecRefs(ctx)
|
||||
return nil, err
|
||||
}
|
||||
if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
|
||||
fs.mu.RUnlock()
|
||||
fs.processDeferredDecRefs(ctx)
|
||||
return nil, err
|
||||
}
|
||||
inode.IncRef()
|
||||
defer inode.DecRef(ctx)
|
||||
fs.mu.RUnlock()
|
||||
fs.processDeferredDecRefs(ctx)
|
||||
return inode.Open(ctx, rp, vfsd, opts)
|
||||
}
|
||||
|
||||
|
@ -414,7 +420,14 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
|||
vfsd := rp.Start()
|
||||
inode := vfsd.Impl().(*Dentry).inode
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
unlocked := false
|
||||
unlock := func() {
|
||||
if !unlocked {
|
||||
fs.mu.Unlock()
|
||||
unlocked = true
|
||||
}
|
||||
}
|
||||
defer unlock()
|
||||
if rp.Done() {
|
||||
if rp.MustBeDir() {
|
||||
return nil, syserror.EISDIR
|
||||
|
@ -425,6 +438,9 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
|||
if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inode.IncRef()
|
||||
defer inode.DecRef(ctx)
|
||||
unlock()
|
||||
return inode.Open(ctx, rp, vfsd, opts)
|
||||
}
|
||||
afterTrailingSymlink:
|
||||
|
@ -466,6 +482,9 @@ afterTrailingSymlink:
|
|||
}
|
||||
child := childVFSD.Impl().(*Dentry)
|
||||
parentVFSD.Impl().(*Dentry).InsertChild(pc, child)
|
||||
child.inode.IncRef()
|
||||
defer child.inode.DecRef(ctx)
|
||||
unlock()
|
||||
return child.inode.Open(ctx, rp, childVFSD, opts)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -499,6 +518,9 @@ afterTrailingSymlink:
|
|||
if err := child.inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
child.inode.IncRef()
|
||||
defer child.inode.DecRef(ctx)
|
||||
unlock()
|
||||
return child.inode.Open(ctx, rp, &child.vfsd, opts)
|
||||
}
|
||||
|
||||
|
|
|
@ -717,17 +717,33 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
|
|||
func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
mayCreate := opts.Flags&linux.O_CREAT != 0
|
||||
mustCreate := opts.Flags&(linux.O_CREAT|linux.O_EXCL) == (linux.O_CREAT | linux.O_EXCL)
|
||||
mayWrite := vfs.AccessTypesForOpenFlags(&opts).MayWrite()
|
||||
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
unlocked := false
|
||||
unlock := func() {
|
||||
if !unlocked {
|
||||
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
unlocked = true
|
||||
}
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
start := rp.Start().Impl().(*dentry)
|
||||
if rp.Done() {
|
||||
if mustCreate {
|
||||
return nil, syserror.EEXIST
|
||||
}
|
||||
return start.openLocked(ctx, rp, &opts)
|
||||
if mayWrite {
|
||||
if err := start.copyUpLocked(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
start.IncRef()
|
||||
defer start.DecRef(ctx)
|
||||
unlock()
|
||||
return start.openCopiedUp(ctx, rp, &opts)
|
||||
}
|
||||
|
||||
afterTrailingSymlink:
|
||||
|
@ -767,20 +783,24 @@ afterTrailingSymlink:
|
|||
start = parent
|
||||
goto afterTrailingSymlink
|
||||
}
|
||||
return child.openLocked(ctx, rp, &opts)
|
||||
if mayWrite {
|
||||
if err := child.copyUpLocked(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
child.IncRef()
|
||||
defer child.DecRef(ctx)
|
||||
unlock()
|
||||
return child.openCopiedUp(ctx, rp, &opts)
|
||||
}
|
||||
|
||||
// Preconditions: fs.renameMu must be locked.
|
||||
func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
// Preconditions: If vfs.AccessTypesForOpenFlags(opts).MayWrite(), then d has
|
||||
// been copied up.
|
||||
func (d *dentry) openCopiedUp(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
ats := vfs.AccessTypesForOpenFlags(opts)
|
||||
if err := d.checkPermissions(rp.Credentials(), ats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ats.MayWrite() {
|
||||
if err := d.copyUpLocked(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
mnt := rp.Mount()
|
||||
|
||||
// Directory FDs open FDs from each layer when directory entries are read,
|
||||
|
@ -792,7 +812,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
|
|||
return nil, syserror.EISDIR
|
||||
}
|
||||
// Can't open directories writably.
|
||||
if ats&vfs.MayWrite != 0 {
|
||||
if ats.MayWrite() {
|
||||
return nil, syserror.EISDIR
|
||||
}
|
||||
if opts.Flags&linux.O_DIRECT != 0 {
|
||||
|
|
|
@ -307,18 +307,28 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
|||
// don't need fs.mu for writing.
|
||||
if opts.Flags&linux.O_CREAT == 0 {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
d, err := resolveLocked(ctx, rp)
|
||||
if err != nil {
|
||||
fs.mu.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
d.IncRef()
|
||||
defer d.DecRef(ctx)
|
||||
fs.mu.RUnlock()
|
||||
return d.open(ctx, rp, &opts, false /* afterCreate */)
|
||||
}
|
||||
|
||||
mustCreate := opts.Flags&linux.O_EXCL != 0
|
||||
start := rp.Start().Impl().(*dentry)
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
unlocked := false
|
||||
unlock := func() {
|
||||
if !unlocked {
|
||||
fs.mu.Unlock()
|
||||
unlocked = true
|
||||
}
|
||||
}
|
||||
defer unlock()
|
||||
if rp.Done() {
|
||||
// Reject attempts to open mount root directory with O_CREAT.
|
||||
if rp.MustBeDir() {
|
||||
|
@ -327,6 +337,9 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
|||
if mustCreate {
|
||||
return nil, syserror.EEXIST
|
||||
}
|
||||
start.IncRef()
|
||||
defer start.DecRef(ctx)
|
||||
unlock()
|
||||
return start.open(ctx, rp, &opts, false /* afterCreate */)
|
||||
}
|
||||
afterTrailingSymlink:
|
||||
|
@ -364,6 +377,7 @@ afterTrailingSymlink:
|
|||
creds := rp.Credentials()
|
||||
child := fs.newDentry(fs.newRegularFile(creds.EffectiveKUID, creds.EffectiveKGID, opts.Mode))
|
||||
parentDir.insertChildLocked(child, name)
|
||||
unlock()
|
||||
fd, err := child.open(ctx, rp, &opts, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -392,9 +406,14 @@ afterTrailingSymlink:
|
|||
if rp.MustBeDir() && !child.inode.isDir() {
|
||||
return nil, syserror.ENOTDIR
|
||||
}
|
||||
child.IncRef()
|
||||
defer child.DecRef(ctx)
|
||||
unlock()
|
||||
return child.open(ctx, rp, &opts, false)
|
||||
}
|
||||
|
||||
// Preconditions: The caller must hold no locks (since opening pipes may block
|
||||
// indefinitely).
|
||||
func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions, afterCreate bool) (*vfs.FileDescription, error) {
|
||||
ats := vfs.AccessTypesForOpenFlags(opts)
|
||||
if !afterCreate {
|
||||
|
|
Loading…
Reference in New Issue