mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Unlock VFS.mountMu before FilesystemImpl calls for /proc/[pid]/{mounts,mountinfo}.
Also move VFS.MakeSyntheticMountpoint() (which is a utility wrapper around VFS.MkdirAllAt(), itself a utility wrapper around VFS.MkdirAt()) to not be in the middle of the implementation of these proc files. Fixes #3878 PiperOrigin-RevId: 330843106
This commit is contained in:
+37
-33
@@ -18,14 +18,12 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
@@ -740,11 +738,23 @@ func (mntns *MountNamespace) Root() VirtualDentry {
|
||||
//
|
||||
// Preconditions: taskRootDir.Ok().
|
||||
func (vfs *VirtualFilesystem) GenerateProcMounts(ctx context.Context, taskRootDir VirtualDentry, buf *bytes.Buffer) {
|
||||
vfs.mountMu.Lock()
|
||||
defer vfs.mountMu.Unlock()
|
||||
rootMnt := taskRootDir.mount
|
||||
|
||||
vfs.mountMu.Lock()
|
||||
mounts := rootMnt.submountsLocked()
|
||||
// Take a reference on mounts since we need to drop vfs.mountMu before
|
||||
// calling vfs.PathnameReachable() (=> FilesystemImpl.PrependPath()).
|
||||
for _, mnt := range mounts {
|
||||
mnt.IncRef()
|
||||
}
|
||||
vfs.mountMu.Unlock()
|
||||
defer func() {
|
||||
for _, mnt := range mounts {
|
||||
mnt.DecRef(ctx)
|
||||
}
|
||||
}()
|
||||
sort.Slice(mounts, func(i, j int) bool { return mounts[i].ID < mounts[j].ID })
|
||||
|
||||
for _, mnt := range mounts {
|
||||
// Get the path to this mount relative to task root.
|
||||
mntRootVD := VirtualDentry{
|
||||
@@ -755,7 +765,7 @@ func (vfs *VirtualFilesystem) GenerateProcMounts(ctx context.Context, taskRootDi
|
||||
if err != nil {
|
||||
// For some reason we didn't get a path. Log a warning
|
||||
// and run with empty path.
|
||||
ctx.Warningf("Error getting pathname for mount root %+v: %v", mnt.root, err)
|
||||
ctx.Warningf("VFS.GenerateProcMounts: error getting pathname for mount root %+v: %v", mnt.root, err)
|
||||
path = ""
|
||||
}
|
||||
if path == "" {
|
||||
@@ -789,11 +799,25 @@ func (vfs *VirtualFilesystem) GenerateProcMounts(ctx context.Context, taskRootDi
|
||||
//
|
||||
// Preconditions: taskRootDir.Ok().
|
||||
func (vfs *VirtualFilesystem) GenerateProcMountInfo(ctx context.Context, taskRootDir VirtualDentry, buf *bytes.Buffer) {
|
||||
vfs.mountMu.Lock()
|
||||
defer vfs.mountMu.Unlock()
|
||||
rootMnt := taskRootDir.mount
|
||||
|
||||
vfs.mountMu.Lock()
|
||||
mounts := rootMnt.submountsLocked()
|
||||
// Take a reference on mounts since we need to drop vfs.mountMu before
|
||||
// calling vfs.PathnameReachable() (=> FilesystemImpl.PrependPath()) or
|
||||
// vfs.StatAt() (=> FilesystemImpl.StatAt()).
|
||||
for _, mnt := range mounts {
|
||||
mnt.IncRef()
|
||||
}
|
||||
vfs.mountMu.Unlock()
|
||||
defer func() {
|
||||
for _, mnt := range mounts {
|
||||
mnt.DecRef(ctx)
|
||||
}
|
||||
}()
|
||||
sort.Slice(mounts, func(i, j int) bool { return mounts[i].ID < mounts[j].ID })
|
||||
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
for _, mnt := range mounts {
|
||||
// Get the path to this mount relative to task root.
|
||||
mntRootVD := VirtualDentry{
|
||||
@@ -804,7 +828,7 @@ func (vfs *VirtualFilesystem) GenerateProcMountInfo(ctx context.Context, taskRoo
|
||||
if err != nil {
|
||||
// For some reason we didn't get a path. Log a warning
|
||||
// and run with empty path.
|
||||
ctx.Warningf("Error getting pathname for mount root %+v: %v", mnt.root, err)
|
||||
ctx.Warningf("VFS.GenerateProcMountInfo: error getting pathname for mount root %+v: %v", mnt.root, err)
|
||||
path = ""
|
||||
}
|
||||
if path == "" {
|
||||
@@ -817,9 +841,10 @@ func (vfs *VirtualFilesystem) GenerateProcMountInfo(ctx context.Context, taskRoo
|
||||
Root: mntRootVD,
|
||||
Start: mntRootVD,
|
||||
}
|
||||
statx, err := vfs.StatAt(ctx, auth.NewAnonymousCredentials(), pop, &StatOptions{})
|
||||
statx, err := vfs.StatAt(ctx, creds, pop, &StatOptions{})
|
||||
if err != nil {
|
||||
// Well that's not good. Ignore this mount.
|
||||
ctx.Warningf("VFS.GenerateProcMountInfo: failed to stat mount root %+v: %v", mnt.root, err)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -831,6 +856,9 @@ func (vfs *VirtualFilesystem) GenerateProcMountInfo(ctx context.Context, taskRoo
|
||||
fmt.Fprintf(buf, "%d ", mnt.ID)
|
||||
|
||||
// (2) Parent ID (or this ID if there is no parent).
|
||||
// Note that even if the call to mnt.parent() races with Mount
|
||||
// destruction (which is possible since we're not holding vfs.mountMu),
|
||||
// its Mount.ID will still be valid.
|
||||
pID := mnt.ID
|
||||
if p := mnt.parent(); p != nil {
|
||||
pID = p.ID
|
||||
@@ -879,30 +907,6 @@ func (vfs *VirtualFilesystem) GenerateProcMountInfo(ctx context.Context, taskRoo
|
||||
}
|
||||
}
|
||||
|
||||
// MakeSyntheticMountpoint creates parent directories of target if they do not
|
||||
// exist and attempts to create a directory for the mountpoint. If a
|
||||
// non-directory file already exists there then we allow it.
|
||||
func (vfs *VirtualFilesystem) MakeSyntheticMountpoint(ctx context.Context, target string, root VirtualDentry, creds *auth.Credentials) error {
|
||||
mkdirOpts := &MkdirOptions{Mode: 0777, ForSyntheticMountpoint: true}
|
||||
|
||||
// Make sure the parent directory of target exists.
|
||||
if err := vfs.MkdirAllAt(ctx, path.Dir(target), root, creds, mkdirOpts); err != nil {
|
||||
return fmt.Errorf("failed to create parent directory of mountpoint %q: %w", target, err)
|
||||
}
|
||||
|
||||
// Attempt to mkdir the final component. If a file (of any type) exists
|
||||
// then we let allow mounting on top of that because we do not require the
|
||||
// target to be an existing directory, unlike Linux mount(2).
|
||||
if err := vfs.MkdirAt(ctx, creds, &PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse(target),
|
||||
}, mkdirOpts); err != nil && err != syserror.EEXIST {
|
||||
return fmt.Errorf("failed to create mountpoint %q: %w", target, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// manglePath replaces ' ', '\t', '\n', and '\\' with their octal equivalents.
|
||||
// See Linux fs/seq_file.c:mangle_path.
|
||||
func manglePath(p string) string {
|
||||
|
||||
@@ -819,6 +819,30 @@ func (vfs *VirtualFilesystem) MkdirAllAt(ctx context.Context, currentPath string
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeSyntheticMountpoint creates parent directories of target if they do not
|
||||
// exist and attempts to create a directory for the mountpoint. If a
|
||||
// non-directory file already exists there then we allow it.
|
||||
func (vfs *VirtualFilesystem) MakeSyntheticMountpoint(ctx context.Context, target string, root VirtualDentry, creds *auth.Credentials) error {
|
||||
mkdirOpts := &MkdirOptions{Mode: 0777, ForSyntheticMountpoint: true}
|
||||
|
||||
// Make sure the parent directory of target exists.
|
||||
if err := vfs.MkdirAllAt(ctx, path.Dir(target), root, creds, mkdirOpts); err != nil {
|
||||
return fmt.Errorf("failed to create parent directory of mountpoint %q: %w", target, err)
|
||||
}
|
||||
|
||||
// Attempt to mkdir the final component. If a file (of any type) exists
|
||||
// then we let allow mounting on top of that because we do not require the
|
||||
// target to be an existing directory, unlike Linux mount(2).
|
||||
if err := vfs.MkdirAt(ctx, creds, &PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse(target),
|
||||
}, mkdirOpts); err != nil && err != syserror.EEXIST {
|
||||
return fmt.Errorf("failed to create mountpoint %q: %w", target, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A VirtualDentry represents a node in a VFS tree, by combining a Dentry
|
||||
// (which represents a node in a Filesystem's tree) and a Mount (which
|
||||
// represents the Filesystem's position in a VFS mount tree).
|
||||
|
||||
Reference in New Issue
Block a user