mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
fsimpl/devpts: handle mode, ptmxmode, uid, gid mount options
PiperOrigin-RevId: 576049946
This commit is contained in:
@@ -47,6 +47,13 @@ type FilesystemType struct {
|
||||
root *vfs.Dentry
|
||||
}
|
||||
|
||||
type fileSystemOpts struct {
|
||||
mode linux.FileMode
|
||||
ptmxMode linux.FileMode
|
||||
uid auth.KUID
|
||||
gid auth.KGID
|
||||
}
|
||||
|
||||
// Name implements vfs.FilesystemType.Name.
|
||||
func (*FilesystemType) Name() string {
|
||||
return Name
|
||||
@@ -54,13 +61,79 @@ func (*FilesystemType) Name() string {
|
||||
|
||||
// GetFilesystem implements vfs.FilesystemType.GetFilesystem.
|
||||
func (fstype *FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
|
||||
// No data allowed.
|
||||
if opts.Data != "" {
|
||||
mopts := vfs.GenericParseMountOptions(opts.Data)
|
||||
fsOpts := fileSystemOpts{
|
||||
mode: 0555,
|
||||
ptmxMode: 0666,
|
||||
uid: creds.EffectiveKUID,
|
||||
gid: creds.EffectiveKGID,
|
||||
}
|
||||
if modeStr, ok := mopts["mode"]; ok {
|
||||
delete(mopts, "mode")
|
||||
mode, err := strconv.ParseUint(modeStr, 8, 32)
|
||||
if err != nil {
|
||||
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid mode: %q", modeStr)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
fsOpts.mode = linux.FileMode(mode & 0777)
|
||||
}
|
||||
if modeStr, ok := mopts["ptmxmode"]; ok {
|
||||
delete(mopts, "ptmxmode")
|
||||
mode, err := strconv.ParseUint(modeStr, 8, 32)
|
||||
if err != nil {
|
||||
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid ptmxmode: %q", modeStr)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
fsOpts.ptmxMode = linux.FileMode(mode & 0777)
|
||||
}
|
||||
if uidStr, ok := mopts["uid"]; ok {
|
||||
delete(mopts, "uid")
|
||||
uid, err := strconv.ParseUint(uidStr, 10, 32)
|
||||
if err != nil {
|
||||
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid uid: %q", uidStr)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
|
||||
if !kuid.Ok() {
|
||||
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
fsOpts.uid = kuid
|
||||
}
|
||||
if gidStr, ok := mopts["gid"]; ok {
|
||||
delete(mopts, "gid")
|
||||
gid, err := strconv.ParseUint(gidStr, 10, 32)
|
||||
if err != nil {
|
||||
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid gid: %q", gidStr)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
|
||||
if !kgid.Ok() {
|
||||
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
fsOpts.gid = kgid
|
||||
}
|
||||
newinstance := false
|
||||
if _, ok := mopts["newinstance"]; ok {
|
||||
newinstance = true
|
||||
delete(mopts, "newinstance")
|
||||
}
|
||||
if len(mopts) != 0 {
|
||||
ctx.Warningf("devpts.FilesystemType.GetFilesystem: unknown options: %v", mopts)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
if newinstance {
|
||||
fs, root, err := fstype.newFilesystem(ctx, vfsObj, creds, fsOpts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return fs.VFSFilesystem(), root.VFSDentry(), nil
|
||||
}
|
||||
|
||||
fstype.initOnce.Do(func() {
|
||||
fs, root, err := fstype.newFilesystem(ctx, vfsObj, creds)
|
||||
fs, root, err := fstype.newFilesystem(ctx, vfsObj, creds, fsOpts)
|
||||
if err != nil {
|
||||
fstype.initErr = err
|
||||
return
|
||||
@@ -93,7 +166,7 @@ type filesystem struct {
|
||||
|
||||
// newFilesystem creates a new devpts filesystem with root directory and ptmx
|
||||
// master inode. It returns the filesystem and root Dentry.
|
||||
func (fstype *FilesystemType) newFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials) (*filesystem, *kernfs.Dentry, error) {
|
||||
func (fstype *FilesystemType) newFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, opts fileSystemOpts) (*filesystem, *kernfs.Dentry, error) {
|
||||
devMinor, err := vfsObj.GetAnonBlockDevMinor()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -108,7 +181,7 @@ func (fstype *FilesystemType) newFilesystem(ctx context.Context, vfsObj *vfs.Vir
|
||||
root := &rootInode{
|
||||
replicas: make(map[uint32]*replicaInode),
|
||||
}
|
||||
root.InodeAttrs.Init(ctx, creds, linux.UNNAMED_MAJOR, devMinor, 1, linux.ModeDirectory|0555)
|
||||
root.InodeAttrs.InitWithIDs(ctx, opts.uid, opts.gid, linux.UNNAMED_MAJOR, devMinor, 1, linux.ModeDirectory|opts.mode)
|
||||
root.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
|
||||
root.InitRefs()
|
||||
|
||||
@@ -120,7 +193,7 @@ func (fstype *FilesystemType) newFilesystem(ctx context.Context, vfsObj *vfs.Vir
|
||||
master := &masterInode{
|
||||
root: root,
|
||||
}
|
||||
master.InodeAttrs.Init(ctx, creds, linux.UNNAMED_MAJOR, devMinor, 2, linux.ModeCharacterDevice|0666)
|
||||
master.InodeAttrs.InitWithIDs(ctx, opts.uid, opts.gid, linux.UNNAMED_MAJOR, devMinor, 2, linux.ModeCharacterDevice|opts.ptmxMode)
|
||||
|
||||
// Add the master as a child of the root.
|
||||
links := root.OrderedChildren.Populate(map[string]kernfs.Inode{
|
||||
|
||||
@@ -191,6 +191,11 @@ type InodeAttrs struct {
|
||||
|
||||
// Init initializes this InodeAttrs.
|
||||
func (a *InodeAttrs) Init(ctx context.Context, creds *auth.Credentials, devMajor, devMinor uint32, ino uint64, mode linux.FileMode) {
|
||||
a.InitWithIDs(ctx, creds.EffectiveKUID, creds.EffectiveKGID, devMajor, devMinor, ino, mode)
|
||||
}
|
||||
|
||||
// InitWithIDs initializes this InodeAttrs.
|
||||
func (a *InodeAttrs) InitWithIDs(ctx context.Context, uid auth.KUID, gid auth.KGID, devMajor, devMinor uint32, ino uint64, mode linux.FileMode) {
|
||||
if mode.FileType() == 0 {
|
||||
panic(fmt.Sprintf("No file type specified in 'mode' for InodeAttrs.Init(): mode=0%o", mode))
|
||||
}
|
||||
@@ -203,8 +208,8 @@ func (a *InodeAttrs) Init(ctx context.Context, creds *auth.Credentials, devMajor
|
||||
a.devMinor = devMinor
|
||||
a.ino.Store(ino)
|
||||
a.mode.Store(uint32(mode))
|
||||
a.uid.Store(uint32(creds.EffectiveKUID))
|
||||
a.gid.Store(uint32(creds.EffectiveKGID))
|
||||
a.uid.Store(uint32(uid))
|
||||
a.gid.Store(uint32(gid))
|
||||
a.nlink.Store(nlink)
|
||||
a.blockSize.Store(hostarch.PageSize)
|
||||
now := ktime.NowFromContext(ctx).Nanoseconds()
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ func registerFilesystems(k *kernel.Kernel, info *containerInfo) error {
|
||||
AllowUserList: true,
|
||||
// TODO(b/29356795): Users may mount this once the terminals are in a
|
||||
// usable state.
|
||||
AllowUserMount: false,
|
||||
AllowUserMount: true,
|
||||
})
|
||||
vfsObj.MustRegisterFilesystemType(devtmpfs.Name, &devtmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
|
||||
AllowUserMount: true,
|
||||
|
||||
@@ -1584,9 +1584,12 @@ cc_binary(
|
||||
"@com_google_absl//absl/time",
|
||||
gtest,
|
||||
"//test/util:cleanup",
|
||||
"//test/util:fs_util",
|
||||
"//test/util:mount_util",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:pty_util",
|
||||
"//test/util:signal_util",
|
||||
"//test/util:temp_path",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
"//test/util:thread_util",
|
||||
|
||||
@@ -38,9 +38,13 @@
|
||||
#include "test/util/capability_util.h"
|
||||
#include "test/util/cleanup.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/linux_capability_util.h"
|
||||
#include "test/util/mount_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/pty_util.h"
|
||||
#include "test/util/signal_util.h"
|
||||
#include "test/util/temp_path.h"
|
||||
#include "test/util/test_util.h"
|
||||
#include "test/util/thread_util.h"
|
||||
|
||||
@@ -585,6 +589,46 @@ TEST(BasicPtyTest, Getdents) {
|
||||
// their usage of the two modes.
|
||||
}
|
||||
|
||||
TEST(BasicPtyTest, NewInstance) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
|
||||
|
||||
auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
auto const mount = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mount("devpts_test", dir.path(), "devpts", 0, "newinstance", 0));
|
||||
auto const dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
auto const mount2 = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mount("devpts_test2", dir.path(), "devpts", 0, "newinstance", 0));
|
||||
|
||||
// Opening PTYs with O_TRUNC shouldn't cause an error, but calls to
|
||||
// (f)truncate should.
|
||||
FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Open(JoinPath(dir.path(), "ptmx"), O_RDWR | O_TRUNC));
|
||||
int n = ASSERT_NO_ERRNO_AND_VALUE(ReplicaID(master));
|
||||
std::string spath2 = absl::StrCat(dir2.path(), "/", n);
|
||||
ASSERT_THAT(open(spath2.c_str(), O_RDWR), SyscallFailsWithErrno(ENOENT));
|
||||
std::string spath = absl::StrCat(dir.path(), "/", n);
|
||||
FileDescriptor replica =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(spath.c_str(), O_RDWR | O_NOCTTY));
|
||||
}
|
||||
|
||||
TEST(BasicPtyTest, SetMode) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
|
||||
|
||||
auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
auto mount = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mount("devpts_test", dir.path(), "devpts", 0,
|
||||
"newinstance,mode=0600,ptmxmode=0620", 0));
|
||||
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Open(JoinPath(dir.path()), O_RDONLY | O_DIRECTORY));
|
||||
mount.Release();
|
||||
|
||||
struct stat st;
|
||||
ASSERT_THAT(fstat(fd.get(), &st), SyscallSucceeds());
|
||||
EXPECT_EQ(st.st_mode, 0600 | S_IFDIR);
|
||||
ASSERT_THAT(fstatat(fd.get(), "ptmx", &st, 0), SyscallSucceeds());
|
||||
EXPECT_EQ(st.st_mode, 0620 | S_IFCHR);
|
||||
}
|
||||
|
||||
class PtyTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
|
||||
Reference in New Issue
Block a user