Implement initial support for MS_REMOUNT to change mount flags.

PiperOrigin-RevId: 572978793
This commit is contained in:
Jing Chen
2023-10-12 12:21:49 -07:00
committed by gVisor bot
parent bc2da4a0c3
commit 3cbedad3da
4 changed files with 209 additions and 132 deletions
+44 -42
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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 {
File diff suppressed because it is too large Load Diff