mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Make mounts ReadWrite first, then later change to ReadOnly.
This lets us create "synthetic" mountpoint directories in ReadOnly mounts during VFS setup. Also add context.WithMountNamespace, as some filesystems (like overlay) require a MountNamespace on ctx to handle vfs.Filesystem Operations. PiperOrigin-RevId: 327874971
This commit is contained in:
committed by
gVisor bot
parent
c9e752b644
commit
5ec3d4ed3e
@@ -63,7 +63,7 @@ func setupDevtmpfs(t *testing.T) (context.Context, *auth.Credentials, *vfs.Virtu
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to create mount point: %v", err)
|
||||
}
|
||||
if err := vfsObj.MountAt(ctx, creds, "devtmpfs" /* source */, &devpop, "devtmpfs" /* fsTypeName */, &vfs.MountOptions{}); err != nil {
|
||||
if _, err := vfsObj.MountAt(ctx, creds, "devtmpfs" /* source */, &devpop, "devtmpfs" /* fsTypeName */, &vfs.MountOptions{}); err != nil {
|
||||
t.Fatalf("failed to mount devtmpfs: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ func mount(b *testing.B, imagePath string, vfsfs *vfs.VirtualFilesystem, pop *vf
|
||||
ctx := contexttest.Context(b)
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
|
||||
if err := vfsfs.MountAt(ctx, creds, imagePath, pop, "extfs", &vfs.MountOptions{
|
||||
if _, err := vfsfs.MountAt(ctx, creds, imagePath, pop, "extfs", &vfs.MountOptions{
|
||||
GetFilesystemOptions: vfs.GetFilesystemOptions{
|
||||
InternalData: int(f.Fd()),
|
||||
},
|
||||
|
||||
@@ -132,7 +132,7 @@ func setup(t *testing.T) *testutil.System {
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := k.VFS().MountAt(ctx, creds, "", pop, Name, mntOpts); err != nil {
|
||||
if _, err := k.VFS().MountAt(ctx, creds, "", pop, Name, mntOpts); err != nil {
|
||||
t.Fatalf("MountAt(/proc): %v", err)
|
||||
}
|
||||
return testutil.NewSystem(ctx, t, k.VFS(), mntns)
|
||||
|
||||
@@ -405,7 +405,7 @@ func BenchmarkVFS2TmpfsMountStat(b *testing.B) {
|
||||
}
|
||||
defer mountPoint.DecRef(ctx)
|
||||
// Create and mount the submount.
|
||||
if err := vfsObj.MountAt(ctx, creds, "", &pop, "tmpfs", &vfs.MountOptions{}); err != nil {
|
||||
if _, err := vfsObj.MountAt(ctx, creds, "", &pop, "tmpfs", &vfs.MountOptions{}); err != nil {
|
||||
b.Fatalf("failed to mount tmpfs submount: %v", err)
|
||||
}
|
||||
filePathBuilder.WriteString(mountPointName)
|
||||
|
||||
@@ -109,8 +109,8 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
return 0, nil, err
|
||||
}
|
||||
defer target.Release(t)
|
||||
|
||||
return 0, nil, t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts)
|
||||
_, err = t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts)
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Umount2 implements Linux syscall umount2(2).
|
||||
|
||||
@@ -40,6 +40,30 @@ func MountNamespaceFromContext(ctx context.Context) *MountNamespace {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mountNamespaceContext struct {
|
||||
context.Context
|
||||
mntns *MountNamespace
|
||||
}
|
||||
|
||||
// Value implements Context.Value.
|
||||
func (mc mountNamespaceContext) Value(key interface{}) interface{} {
|
||||
switch key {
|
||||
case CtxMountNamespace:
|
||||
mc.mntns.IncRef()
|
||||
return mc.mntns
|
||||
default:
|
||||
return mc.Context.Value(key)
|
||||
}
|
||||
}
|
||||
|
||||
// WithMountNamespace returns a copy of ctx with the given MountNamespace.
|
||||
func WithMountNamespace(ctx context.Context, mntns *MountNamespace) context.Context {
|
||||
return &mountNamespaceContext{
|
||||
Context: ctx,
|
||||
mntns: mntns,
|
||||
}
|
||||
}
|
||||
|
||||
// RootFromContext returns the VFS root used by ctx. It takes a reference on
|
||||
// the returned VirtualDentry. If ctx does not have a specific VFS root,
|
||||
// RootFromContext returns a zero-value VirtualDentry.
|
||||
|
||||
+15
-4
@@ -263,16 +263,20 @@ func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Cr
|
||||
}
|
||||
|
||||
// MountAt creates and mounts a Filesystem configured by the given arguments.
|
||||
func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentials, source string, target *PathOperation, fsTypeName string, opts *MountOptions) error {
|
||||
// The VirtualFilesystem will hold a reference to the Mount until it is unmounted.
|
||||
//
|
||||
// This method returns the mounted Mount without a reference, for convenience
|
||||
// during VFS setup when there is no chance of racing with unmount.
|
||||
func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentials, source string, target *PathOperation, fsTypeName string, opts *MountOptions) (*Mount, error) {
|
||||
mnt, err := vfs.MountDisconnected(ctx, creds, source, fsTypeName, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer mnt.DecRef(ctx)
|
||||
if err := vfs.ConnectMountAt(ctx, creds, mnt, target); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
return mnt, nil
|
||||
}
|
||||
|
||||
// UmountAt removes the Mount at the given path.
|
||||
@@ -657,6 +661,13 @@ retryFirst:
|
||||
return VirtualDentry{mnt, d}
|
||||
}
|
||||
|
||||
// SetMountReadOnly sets the mount as ReadOnly.
|
||||
func (vfs *VirtualFilesystem) SetMountReadOnly(mnt *Mount, ro bool) error {
|
||||
vfs.mountMu.Lock()
|
||||
defer vfs.mountMu.Unlock()
|
||||
return mnt.setReadOnlyLocked(ro)
|
||||
}
|
||||
|
||||
// CheckBeginWrite increments the counter of in-progress write operations on
|
||||
// mnt. If mnt is mounted MS_RDONLY, CheckBeginWrite does nothing and returns
|
||||
// EROFS.
|
||||
|
||||
+38
-17
@@ -205,15 +205,34 @@ func (c *containerMounter) mountSubmountsVFS2(ctx context.Context, conf *config.
|
||||
for i := range mounts {
|
||||
submount := &mounts[i]
|
||||
log.Debugf("Mounting %q to %q, type: %s, options: %s", submount.Source, submount.Destination, submount.Type, submount.Options)
|
||||
var (
|
||||
mnt *vfs.Mount
|
||||
err error
|
||||
)
|
||||
|
||||
if hint := c.hints.findMount(submount.Mount); hint != nil && hint.isSupported() {
|
||||
if err := c.mountSharedSubmountVFS2(ctx, conf, mns, creds, submount.Mount, hint); err != nil {
|
||||
mnt, err = c.mountSharedSubmountVFS2(ctx, conf, mns, creds, submount.Mount, hint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mount shared mount %q to %q: %v", hint.name, submount.Destination, err)
|
||||
}
|
||||
} else {
|
||||
if err := c.mountSubmountVFS2(ctx, conf, mns, creds, submount); err != nil {
|
||||
mnt, err = c.mountSubmountVFS2(ctx, conf, mns, creds, submount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mount submount %q: %w", submount.Destination, err)
|
||||
}
|
||||
}
|
||||
|
||||
if mnt != nil && mnt.ReadOnly() {
|
||||
// Switch to ReadWrite while we setup submounts.
|
||||
if err := c.k.VFS().SetMountReadOnly(mnt, false); err != nil {
|
||||
return fmt.Errorf("failed to set mount at %q readwrite: %v", submount.Destination, err)
|
||||
}
|
||||
defer func() {
|
||||
if err := c.k.VFS().SetMountReadOnly(mnt, true); err != nil {
|
||||
panic(fmt.Sprintf("failed to restore mount at %q back to readonly: %v", submount.Destination, err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.mountTmpVFS2(ctx, conf, creds, mns); err != nil {
|
||||
@@ -256,7 +275,7 @@ func (c *containerMounter) prepareMountsVFS2() ([]mountAndFD, error) {
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
func (c *containerMounter) mountSubmountVFS2(ctx context.Context, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, submount *mountAndFD) error {
|
||||
func (c *containerMounter) mountSubmountVFS2(ctx context.Context, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, submount *mountAndFD) (*vfs.Mount, error) {
|
||||
root := mns.Root()
|
||||
defer root.DecRef(ctx)
|
||||
target := &vfs.PathOperation{
|
||||
@@ -266,21 +285,22 @@ func (c *containerMounter) mountSubmountVFS2(ctx context.Context, conf *config.C
|
||||
}
|
||||
fsName, opts, err := c.getMountNameAndOptionsVFS2(conf, submount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mountOptions failed: %w", err)
|
||||
return nil, fmt.Errorf("mountOptions failed: %w", err)
|
||||
}
|
||||
if len(fsName) == 0 {
|
||||
// Filesystem is not supported (e.g. cgroup), just skip it.
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := c.k.VFS().MkdirAllAt(ctx, submount.Destination, root, creds, &vfs.MkdirOptions{Mode: 0777, ForSyntheticMountpoint: true}); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if err := c.k.VFS().MountAt(ctx, creds, "", target, fsName, opts); err != nil {
|
||||
return fmt.Errorf("failed to mount %q (type: %s): %w, opts: %v", submount.Destination, submount.Type, err, opts)
|
||||
mnt, err := c.k.VFS().MountAt(ctx, creds, "", target, fsName, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to mount %q (type: %s): %w, opts: %v", submount.Destination, submount.Type, err, opts)
|
||||
}
|
||||
log.Infof("Mounted %q to %q type: %s, internal-options: %q", submount.Source, submount.Destination, submount.Type, opts.GetFilesystemOptions.Data)
|
||||
return nil
|
||||
return mnt, nil
|
||||
}
|
||||
|
||||
// getMountNameAndOptionsVFS2 retrieves the fsName, opts, and useOverlay values
|
||||
@@ -407,7 +427,8 @@ func (c *containerMounter) mountTmpVFS2(ctx context.Context, conf *config.Config
|
||||
// another user. This is normally done for /tmp.
|
||||
Options: []string{"mode=01777"},
|
||||
}
|
||||
return c.mountSubmountVFS2(ctx, conf, mns, creds, &mountAndFD{Mount: tmpMount})
|
||||
_, err := c.mountSubmountVFS2(ctx, conf, mns, creds, &mountAndFD{Mount: tmpMount})
|
||||
return err
|
||||
|
||||
case syserror.ENOTDIR:
|
||||
// Not a dir?! Let it be.
|
||||
@@ -458,25 +479,25 @@ func (c *containerMounter) mountSharedMasterVFS2(ctx context.Context, conf *conf
|
||||
|
||||
// mountSharedSubmount binds mount to a previously mounted volume that is shared
|
||||
// among containers in the same pod.
|
||||
func (c *containerMounter) mountSharedSubmountVFS2(ctx context.Context, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, mount specs.Mount, source *mountHint) error {
|
||||
func (c *containerMounter) mountSharedSubmountVFS2(ctx context.Context, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, mount specs.Mount, source *mountHint) (*vfs.Mount, error) {
|
||||
if err := source.checkCompatible(mount); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, opts, err := c.getMountNameAndOptionsVFS2(conf, &mountAndFD{Mount: mount})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
newMnt, err := c.k.VFS().NewDisconnectedMount(source.vfsMount.Filesystem(), source.vfsMount.Root(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer newMnt.DecRef(ctx)
|
||||
|
||||
root := mns.Root()
|
||||
defer root.DecRef(ctx)
|
||||
if err := c.k.VFS().MkdirAllAt(ctx, mount.Destination, root, creds, &vfs.MkdirOptions{Mode: 0777, ForSyntheticMountpoint: true}); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
target := &vfs.PathOperation{
|
||||
@@ -485,8 +506,8 @@ func (c *containerMounter) mountSharedSubmountVFS2(ctx context.Context, conf *co
|
||||
Path: fspath.Parse(mount.Destination),
|
||||
}
|
||||
if err := c.k.VFS().ConnectMountAt(ctx, creds, newMnt, target); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("Mounted %q type shared bind to %q", mount.Destination, source.name)
|
||||
return nil
|
||||
return newMnt, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user