mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
gVisor bot
parent
61e742b389
commit
f53c349d51
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user