Add support for bind mounting.

This change adds limited support for the MS_BIND flag. MS_REC is not yet
implemented.

PiperOrigin-RevId: 478030021
This commit is contained in:
Lucas Manning
2022-09-30 10:52:34 -07:00
committed by gVisor bot
parent 61e742b389
commit f53c349d51
3 changed files with 102 additions and 4 deletions
+20 -4
View File
@@ -17,6 +17,7 @@ package vfs2
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"
@@ -71,9 +72,8 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return 0, nil, linuxerr.EPERM
}
const unsupportedOps = linux.MS_REMOUNT | linux.MS_BIND |
linux.MS_SHARED | linux.MS_PRIVATE | linux.MS_SLAVE |
linux.MS_UNBINDABLE | linux.MS_MOVE
const unsupportedOps = linux.MS_REMOUNT | linux.MS_SHARED | linux.MS_PRIVATE |
linux.MS_SLAVE | linux.MS_UNBINDABLE | linux.MS_MOVE
// Silently allow MS_NOSUID, since we don't implement set-id bits
// anyway.
@@ -109,7 +109,23 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return 0, nil, err
}
defer target.Release(t)
_, err = t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts)
if flags&linux.MS_BIND == linux.MS_BIND {
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, nofollowFinalSymlink)
if err != nil {
return 0, nil, err
}
defer sourceTpop.Release(t)
_, err = t.Kernel().VFS().BindAt(t, creds, &sourceTpop.pop, &target.pop)
} else {
_, err = t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts)
}
return 0, nil, err
}
+20
View File
@@ -271,6 +271,26 @@ func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Cr
return nil
}
// BindAt creates a clone of the source path's parent mount and mounts it at
// the target path. The new mount's root dentry is one pointed to by the source
// path.
//
// TODO(b/249121230): Support recursive bind mounting.
func (vfs *VirtualFilesystem) BindAt(ctx context.Context, creds *auth.Credentials, source, target *PathOperation) (*Mount, error) {
vd, err := vfs.GetDentryAt(ctx, creds, source, &GetDentryOptions{})
if err != nil {
return nil, err
}
defer vd.DecRef(ctx)
opts := vd.mount.Options()
mnt := vfs.NewDisconnectedMount(vd.mount.fs, vd.dentry, &opts)
defer mnt.DecRef(ctx)
if err := vfs.ConnectMountAt(ctx, creds, mnt, target); err != nil {
return nil, err
}
return mnt, nil
}
// MountAt creates and mounts a Filesystem configured by the given arguments.
// The VirtualFilesystem will hold a reference to the Mount until it is
// unmounted.
+62
View File
@@ -759,6 +759,68 @@ TEST(MountTest, TmpfsSizeMmap) {
::testing::KilledBySignal(SIGBUS), "");
EXPECT_THAT(munmap(addr, 2 * kPageSize), SyscallSucceeds());
}
TEST(MountTest, SimpleBind) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
auto const dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
auto const dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
auto const mount = ASSERT_NO_ERRNO_AND_VALUE(
Mount("", dir1.path(), "tmpfs", 0, "mode=0123", 0));
auto const child1 =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path()));
auto const child2 =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path()));
auto const bind_mount = Mount(dir1.path(), dir2.path(), "", MS_BIND, "", 0);
// Write to child1 in dir1.
const std::string filename = "foo.txt";
const std::string contents = "barbaz";
ASSERT_NO_ERRNO(CreateWithContents(JoinPath(child1.path(), filename),
contents, O_WRONLY));
// Verify both directories have the same nodes.
std::vector<std::string> child_names = {std::string(Basename(child1.path())),
std::string(Basename(child2.path()))};
ASSERT_NO_ERRNO(DirContains(dir1.path(), child_names, {}));
ASSERT_NO_ERRNO(DirContains(dir2.path(), child_names, {}));
const std::string dir1_filepath =
JoinPath(dir1.path(), Basename(child1.path()), filename);
const std::string dir2_filepath =
JoinPath(dir2.path(), Basename(child1.path()), filename);
std::string output;
ASSERT_NO_ERRNO(GetContents(dir1_filepath, &output));
ASSERT_TRUE(output == contents);
ASSERT_NO_ERRNO(GetContents(dir2_filepath, &output));
ASSERT_TRUE(output == contents);
}
TEST(MountTest, BindToSelf) {
// Test that we can turn a normal directory into a mount with MS_BIND.
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const std::vector<ProcMountsEntry> mounts_before =
ASSERT_NO_ERRNO_AND_VALUE(ProcSelfMountsEntries());
for (const auto& e : mounts_before) {
ASSERT_TRUE(e.mount_point != dir.path());
}
auto const mount = ASSERT_NO_ERRNO_AND_VALUE(
Mount(dir.path(), dir.path(), "", MS_BIND, "", 0));
const std::vector<ProcMountsEntry> mounts_after =
ASSERT_NO_ERRNO_AND_VALUE(ProcSelfMountsEntries());
bool found = false;
for (const auto& e : mounts_after) {
if (e.mount_point == dir.path()) {
found = true;
}
}
ASSERT_TRUE(found);
}
} // namespace
} // namespace testing