fsimpl/devpts: handle mode, ptmxmode, uid, gid mount options

PiperOrigin-RevId: 576049946
This commit is contained in:
Andrei Vagin
2023-10-24 00:56:23 -07:00
committed by gVisor bot
parent f62a4a77d5
commit 639488b65a
5 changed files with 134 additions and 9 deletions
+79 -6
View File
@@ -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{
+7 -2
View File
@@ -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
View File
@@ -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,
+3
View File
@@ -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",
+44
View File
@@ -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 {