mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement initial support for MS_REMOUNT to change mount flags.
PiperOrigin-RevId: 572978793
This commit is contained in:
@@ -17,7 +17,6 @@ package linux
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
@@ -45,8 +44,7 @@ func Mount(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
|
||||
}
|
||||
|
||||
// Silently allow MS_NOSUID, since we don't implement set-id bits anyway.
|
||||
const unsupported = linux.MS_REMOUNT | linux.MS_UNBINDABLE | linux.MS_MOVE |
|
||||
linux.MS_NODIRATIME
|
||||
const unsupported = linux.MS_UNBINDABLE | linux.MS_MOVE | linux.MS_NODIRATIME
|
||||
|
||||
// Linux just allows passing any flags to mount(2) - it won't fail when
|
||||
// unknown or unsupported flags are passed. Since we don't implement
|
||||
@@ -66,45 +64,6 @@ func Mount(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
|
||||
return 0, nil, err
|
||||
}
|
||||
defer target.Release(t)
|
||||
|
||||
if flags&linux.MS_BIND != 0 {
|
||||
var sourcePath fspath.Path
|
||||
sourcePath, err = copyInPath(t, sourceAddr)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var sourceTpop taskPathOperation
|
||||
sourceTpop, err = getTaskPathOperation(t, linux.AT_FDCWD, sourcePath, disallowEmptyPath, followFinalSymlink)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer sourceTpop.Release(t)
|
||||
return 0, nil, t.Kernel().VFS().BindAt(t, creds, &sourceTpop.pop, &target.pop, flags&linux.MS_REC != 0)
|
||||
}
|
||||
if flags&(linux.MS_SHARED|linux.MS_PRIVATE|linux.MS_SLAVE|linux.MS_UNBINDABLE) != 0 {
|
||||
return 0, nil, t.Kernel().VFS().SetMountPropagationAt(t, creds, &target.pop, uint32(flags))
|
||||
}
|
||||
|
||||
// Only copy in source, fstype, and data if we are doing a normal mount.
|
||||
source, err := t.CopyInString(sourceAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
fsType, err := t.CopyInString(typeAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
data := ""
|
||||
if dataAddr != 0 {
|
||||
// In Linux, a full page is always copied in regardless of null
|
||||
// character placement, and the address is passed to each file system.
|
||||
// Most file systems always treat this data as a string, though, and so
|
||||
// do all of the ones we implement.
|
||||
data, err = t.CopyInString(dataAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
var opts vfs.MountOptions
|
||||
if flags&(linux.MS_NOATIME|linux.MS_STRICTATIME) == linux.MS_NOATIME {
|
||||
opts.Flags.NoATime = true
|
||||
@@ -121,7 +80,50 @@ func Mount(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
|
||||
if flags&linux.MS_RDONLY == linux.MS_RDONLY {
|
||||
opts.ReadOnly = true
|
||||
}
|
||||
data := ""
|
||||
if dataAddr != 0 {
|
||||
// In Linux, a full page is always copied in regardless of null
|
||||
// character placement, and the address is passed to each file system.
|
||||
// Most file systems always treat this data as a string, though, and so
|
||||
// do all of the ones we implement.
|
||||
data, err = t.CopyInString(dataAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
opts.GetFilesystemOptions.Data = data
|
||||
switch {
|
||||
case flags&linux.MS_REMOUNT != 0:
|
||||
// When MS_REMOUNT is specified, the flags and data should match the values used in the original mount() call,
|
||||
// except for those parameters that are being changed.
|
||||
//
|
||||
// The src and filesystem type are ignored for MS_REMOUNT.
|
||||
return 0, nil, t.Kernel().VFS().RemountAt(t, creds, &target.pop, &opts)
|
||||
case flags&linux.MS_BIND != 0:
|
||||
sourcePath, err := copyInPath(t, sourceAddr)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var sourceTpop taskPathOperation
|
||||
sourceTpop, err = getTaskPathOperation(t, linux.AT_FDCWD, sourcePath, disallowEmptyPath, followFinalSymlink)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer sourceTpop.Release(t)
|
||||
return 0, nil, t.Kernel().VFS().BindAt(t, creds, &sourceTpop.pop, &target.pop, flags&linux.MS_REC != 0)
|
||||
case flags&(linux.MS_SHARED|linux.MS_PRIVATE|linux.MS_SLAVE|linux.MS_UNBINDABLE) != 0:
|
||||
return 0, nil, t.Kernel().VFS().SetMountPropagationAt(t, creds, &target.pop, uint32(flags))
|
||||
}
|
||||
|
||||
// Only copy in source, fstype, and data if we are doing a normal mount.
|
||||
source, err := t.CopyInString(sourceAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
fsType, err := t.CopyInString(typeAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
_, err = t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts)
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
+44
-1
@@ -149,6 +149,21 @@ func (mnt *Mount) Options() MountOptions {
|
||||
}
|
||||
}
|
||||
|
||||
// setMountOptions sets mnt's opions to the given opts.
|
||||
//
|
||||
// Preconditions:
|
||||
// - vfs.mountMu must be locked.
|
||||
func (mnt *Mount) setMountOptions(opts *MountOptions) error {
|
||||
if opts == nil {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
if err := mnt.setReadOnlyLocked(opts.ReadOnly); err != nil {
|
||||
return err
|
||||
}
|
||||
mnt.Flags = opts.Flags
|
||||
return nil
|
||||
}
|
||||
|
||||
// MountFlags returns a bit mask that indicates mount options.
|
||||
func (mnt *Mount) MountFlags() uint64 {
|
||||
mnt.vfs.lockMounts()
|
||||
@@ -502,6 +517,35 @@ func (vfs *VirtualFilesystem) BindAt(ctx context.Context, creds *auth.Credential
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemountAt changes the mountflags and data of an existing mount without having to unmount and remount the filesystem.
|
||||
func (vfs *VirtualFilesystem) RemountAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, opts *MountOptions) error {
|
||||
vd, err := vfs.GetDentryAt(ctx, creds, pop, &GetDentryOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
vd.DecRef(ctx)
|
||||
}()
|
||||
if vd.dentry.isMounted() {
|
||||
if realmnt := vfs.getMountAt(ctx, vd.mount, vd.dentry); realmnt != nil {
|
||||
vd.mount.DecRef(ctx)
|
||||
vd.mount = realmnt
|
||||
}
|
||||
} else if vd.dentry != vd.mount.root {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
vfs.lockMounts()
|
||||
defer vfs.unlockMounts(ctx)
|
||||
mnt := vd.Mount()
|
||||
if mntns := MountNamespaceFromContext(ctx); mntns != nil {
|
||||
vfs.delayDecRef(mntns)
|
||||
if mntns != mnt.ns {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
return mnt.setMountOptions(opts)
|
||||
}
|
||||
|
||||
// MountAt creates and mounts a Filesystem configured by the given arguments.
|
||||
// The VirtualFilesystem will hold a reference to the Mount until it is
|
||||
// unmounted.
|
||||
@@ -533,7 +577,6 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
|
||||
if opts.Flags&linux.MNT_FORCE != 0 && creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, creds.UserNamespace.Root()) {
|
||||
return linuxerr.EPERM
|
||||
}
|
||||
|
||||
vd, err := vfs.GetDentryAt(ctx, creds, pop, &GetDentryOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -100,7 +100,7 @@ type MountFlags struct {
|
||||
NoSUID bool
|
||||
}
|
||||
|
||||
// MountOptions contains options to VirtualFilesystem.MountAt().
|
||||
// MountOptions contains options to VirtualFilesystem.MountAt(), and VirtualFilesystem.RemountAt()
|
||||
//
|
||||
// +stateify savable
|
||||
type MountOptions struct {
|
||||
|
||||
+120
-88
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user