diff options
Diffstat (limited to 'pkg/sentry/fs/dev')
-rw-r--r-- | pkg/sentry/fs/dev/BUILD | 53 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/dev.go | 122 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/device.go | 20 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/fs.go | 90 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/full.go | 53 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/null.go | 96 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/random.go | 55 |
7 files changed, 489 insertions, 0 deletions
diff --git a/pkg/sentry/fs/dev/BUILD b/pkg/sentry/fs/dev/BUILD new file mode 100644 index 000000000..42049ecb5 --- /dev/null +++ b/pkg/sentry/fs/dev/BUILD @@ -0,0 +1,53 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//tools/go_stateify:defs.bzl", "go_stateify") + +go_stateify( + name = "dev_state", + srcs = [ + "dev.go", + "fs.go", + "full.go", + "null.go", + "random.go", + ], + out = "dev_state.go", + package = "dev", +) + +go_library( + name = "dev", + srcs = [ + "dev.go", + "dev_state.go", + "device.go", + "fs.go", + "full.go", + "null.go", + "random.go", + ], + importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/fs/dev", + visibility = ["//pkg/sentry:internal"], + deps = [ + "//pkg/abi/linux", + "//pkg/amutex", + "//pkg/log", + "//pkg/sentry/context", + "//pkg/sentry/device", + "//pkg/sentry/fs", + "//pkg/sentry/fs/ashmem", + "//pkg/sentry/fs/binder", + "//pkg/sentry/fs/fsutil", + "//pkg/sentry/fs/ramfs", + "//pkg/sentry/fs/tmpfs", + "//pkg/sentry/memmap", + "//pkg/sentry/mm", + "//pkg/sentry/platform", + "//pkg/sentry/safemem", + "//pkg/sentry/usage", + "//pkg/sentry/usermem", + "//pkg/state", + "//pkg/syserror", + ], +) diff --git a/pkg/sentry/fs/dev/dev.go b/pkg/sentry/fs/dev/dev.go new file mode 100644 index 000000000..36c61bfc2 --- /dev/null +++ b/pkg/sentry/fs/dev/dev.go @@ -0,0 +1,122 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package dev provides a filesystem with simple devices. +package dev + +import ( + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ashmem" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/binder" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/tmpfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/platform" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" +) + +// Dev is the root node. +type Dev struct { + ramfs.Dir +} + +func newCharacterDevice(iops fs.InodeOperations, msrc *fs.MountSource) *fs.Inode { + return fs.NewInode(iops, msrc, fs.StableAttr{ + DeviceID: devDevice.DeviceID(), + InodeID: devDevice.NextIno(), + BlockSize: usermem.PageSize, + Type: fs.CharacterDevice, + }) +} + +func newDirectory(ctx context.Context, msrc *fs.MountSource) *fs.Inode { + iops := &ramfs.Dir{} + iops.InitDir(ctx, map[string]*fs.Inode{}, fs.RootOwner, fs.FilePermsFromMode(0555)) + return fs.NewInode(iops, msrc, fs.StableAttr{ + DeviceID: devDevice.DeviceID(), + InodeID: devDevice.NextIno(), + BlockSize: usermem.PageSize, + Type: fs.Directory, + }) +} + +func newSymlink(ctx context.Context, target string, msrc *fs.MountSource) *fs.Inode { + iops := &ramfs.Symlink{} + iops.InitSymlink(ctx, fs.RootOwner, target) + return fs.NewInode(iops, msrc, fs.StableAttr{ + DeviceID: devDevice.DeviceID(), + InodeID: devDevice.NextIno(), + BlockSize: usermem.PageSize, + Type: fs.Symlink, + }) +} + +// New returns the root node of a device filesystem. +func New(ctx context.Context, msrc *fs.MountSource, binderEnabled bool, ashmemEnabled bool) *fs.Inode { + d := &Dev{} + + contents := map[string]*fs.Inode{ + "fd": newSymlink(ctx, "/proc/self/fd", msrc), + "stdin": newSymlink(ctx, "/proc/self/fd/0", msrc), + "stdout": newSymlink(ctx, "/proc/self/fd/1", msrc), + "stderr": newSymlink(ctx, "/proc/self/fd/2", msrc), + + "null": newCharacterDevice(newNullDevice(ctx, fs.RootOwner, 0666), msrc), + "zero": newCharacterDevice(newZeroDevice(ctx, fs.RootOwner, 0666), msrc), + "full": newCharacterDevice(newFullDevice(ctx, fs.RootOwner, 0666), msrc), + + // This is not as good as /dev/random in linux because go + // runtime uses sys_random and /dev/urandom internally. + // According to 'man 4 random', this will be sufficient unless + // application uses this to generate long-lived GPG/SSL/SSH + // keys. + "random": newCharacterDevice(newRandomDevice(ctx, fs.RootOwner, 0444), msrc), + "urandom": newCharacterDevice(newRandomDevice(ctx, fs.RootOwner, 0444), msrc), + + "shm": tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, platform.FromContext(ctx)), + + // A devpts is typically mounted at /dev/pts to provide + // pseudoterminal support. Place an empty directory there for + // the devpts to be mounted over. + "pts": newDirectory(ctx, msrc), + // Similarly, applications expect a ptmx device at /dev/ptmx + // connected to the terminals provided by /dev/pts/. Rather + // than creating a device directly (which requires a hairy + // lookup on open to determine if a devpts exists), just create + // a symlink to the ptmx provided by devpts. (The Linux devpts + // documentation recommends this). + // + // If no devpts is mounted, this will simply be a dangling + // symlink, which is fine. + "ptmx": newSymlink(ctx, "pts/ptmx", msrc), + } + + if binderEnabled { + binder := binder.NewDevice(ctx, fs.RootOwner, fs.FilePermsFromMode(0666)) + contents["binder"] = newCharacterDevice(binder, msrc) + } + + if ashmemEnabled { + ashmem := ashmem.NewDevice(ctx, fs.RootOwner, fs.FilePermsFromMode(0666)) + contents["ashmem"] = newCharacterDevice(ashmem, msrc) + } + + d.InitDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555)) + return fs.NewInode(d, msrc, fs.StableAttr{ + DeviceID: devDevice.DeviceID(), + InodeID: devDevice.NextIno(), + BlockSize: usermem.PageSize, + Type: fs.Directory, + }) +} diff --git a/pkg/sentry/fs/dev/device.go b/pkg/sentry/fs/dev/device.go new file mode 100644 index 000000000..9d935e008 --- /dev/null +++ b/pkg/sentry/fs/dev/device.go @@ -0,0 +1,20 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev + +import "gvisor.googlesource.com/gvisor/pkg/sentry/device" + +// devDevice is the pseudo-filesystem device. +var devDevice = device.NewAnonDevice() diff --git a/pkg/sentry/fs/dev/fs.go b/pkg/sentry/fs/dev/fs.go new file mode 100644 index 000000000..4945ac962 --- /dev/null +++ b/pkg/sentry/fs/dev/fs.go @@ -0,0 +1,90 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev + +import ( + "strconv" + + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/syserror" +) + +// Optional key containing boolean flag which specifies if Android Binder IPC should be enabled. +const binderEnabledKey = "binder_enabled" + +// Optional key containing boolean flag which specifies if Android ashmem should be enabled. +const ashmemEnabledKey = "ashmem_enabled" + +// filesystem is a devtmpfs. +type filesystem struct{} + +func init() { + fs.RegisterFilesystem(&filesystem{}) +} + +// FilesystemName is the name underwhich the filesystem is registered. +// Name matches drivers/base/devtmpfs.c:dev_fs_type.name. +const FilesystemName = "devtmpfs" + +// Name is the name of the file system. +func (*filesystem) Name() string { + return FilesystemName +} + +// AllowUserMount allows users to mount(2) this file system. +func (*filesystem) AllowUserMount() bool { + return true +} + +// Flags returns that there is nothing special about this file system. +// +// In Linux, devtmpfs does the same thing. +func (*filesystem) Flags() fs.FilesystemFlags { + return 0 +} + +// Mount returns a devtmpfs root that can be positioned in the vfs. +func (f *filesystem) Mount(ctx context.Context, device string, flags fs.MountSourceFlags, data string) (*fs.Inode, error) { + // device is always ignored. + // devtmpfs backed by ramfs ignores bad options. See fs/ramfs/inode.c:ramfs_parse_options. + // -> we should consider parsing the mode and backing devtmpfs by this. + + // Parse generic comma-separated key=value options. + options := fs.GenericMountSourceOptions(data) + + // binerEnabledKey is optional and binder is disabled by default. + binderEnabled := false + if beStr, exists := options[binderEnabledKey]; exists { + var err error + binderEnabled, err = strconv.ParseBool(beStr) + if err != nil { + return nil, syserror.EINVAL + } + } + + // ashmemEnabledKey is optional and ashmem is disabled by default. + ashmemEnabled := false + if aeStr, exists := options[ashmemEnabledKey]; exists { + var err error + ashmemEnabled, err = strconv.ParseBool(aeStr) + if err != nil { + return nil, syserror.EINVAL + } + } + + // Construct the devtmpfs root. + return New(ctx, fs.NewNonCachingMountSource(f, flags), binderEnabled, ashmemEnabled), nil +} diff --git a/pkg/sentry/fs/dev/full.go b/pkg/sentry/fs/dev/full.go new file mode 100644 index 000000000..e13eb6c03 --- /dev/null +++ b/pkg/sentry/fs/dev/full.go @@ -0,0 +1,53 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev + +import ( + "math" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserror" +) + +// fullDevice is used to implement /dev/full. +type fullDevice struct { + ramfs.Entry +} + +func newFullDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *fullDevice { + f := &fullDevice{} + f.InitEntry(ctx, owner, fs.FilePermsFromMode(mode)) + return f +} + +// DeprecatedPwritev implements fs.InodeOperations.DeprecatedPwritev by +// returining ENOSPC. +func (f *fullDevice) DeprecatedPwritev(_ context.Context, _ usermem.IOSequence, _ int64) (int64, error) { + return 0, syserror.ENOSPC +} + +// DeprecatedPreadv implements fs.InodeOperations.DeprecatedPreadv. +func (f *fullDevice) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, _ int64) (int64, error) { + return dst.ZeroOut(ctx, math.MaxInt64) +} + +// Truncate should be simply ignored for character devices on linux. +func (f *fullDevice) Truncate(context.Context, *fs.Inode, int64) error { + return nil +} diff --git a/pkg/sentry/fs/dev/null.go b/pkg/sentry/fs/dev/null.go new file mode 100644 index 000000000..66b8ba967 --- /dev/null +++ b/pkg/sentry/fs/dev/null.go @@ -0,0 +1,96 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev + +import ( + "io" + "math" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/memmap" + "gvisor.googlesource.com/gvisor/pkg/sentry/mm" + "gvisor.googlesource.com/gvisor/pkg/sentry/platform" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" +) + +type nullDevice struct { + ramfs.Entry +} + +func newNullDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *nullDevice { + n := &nullDevice{} + n.InitEntry(ctx, owner, fs.FilePermsFromMode(mode)) + return n +} + +// DeprecatedPreadv reads data from the device. +func (n *nullDevice) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) { + return 0, io.EOF +} + +// DeprecatedPwritev discards writes. +func (n *nullDevice) DeprecatedPwritev(_ context.Context, src usermem.IOSequence, offset int64) (int64, error) { + return src.NumBytes(), nil +} + +// Truncate should be simply ignored for character devices on linux. +func (n *nullDevice) Truncate(context.Context, *fs.Inode, int64) error { + return nil +} + +type zeroDevice struct { + nullDevice +} + +func newZeroDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *zeroDevice { + zd := &zeroDevice{} + zd.InitEntry(ctx, owner, fs.FilePermsFromMode(mode)) + return zd +} + +// DeprecatedPreadv implements fs.InodeOperations.DeprecatedPreadv. +func (zd *zeroDevice) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) { + return dst.ZeroOut(ctx, math.MaxInt64) +} + +// GetFile overrides ramfs.Entry.GetFile and returns a zeroFile instead. +func (zd *zeroDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { + // Allow pread(2) and pwrite(2) on this file. + flags.Pread = true + flags.Pwrite = true + + return fs.NewFile(ctx, dirent, flags, &zeroFileOperations{ + FileOperations: &fsutil.Handle{HandleOperations: dirent.Inode.HandleOps()}, + }), nil +} + +type zeroFileOperations struct { + fs.FileOperations +} + +// ConfigureMMap implements fs.FileOperations.ConfigureMMap. +func (*zeroFileOperations) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.MMapOpts) error { + m, err := mm.NewSharedAnonMappable(opts.Length, platform.FromContext(ctx)) + if err != nil { + return err + } + opts.MappingIdentity = m + opts.Mappable = m + return nil +} diff --git a/pkg/sentry/fs/dev/random.go b/pkg/sentry/fs/dev/random.go new file mode 100644 index 000000000..0402f9355 --- /dev/null +++ b/pkg/sentry/fs/dev/random.go @@ -0,0 +1,55 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev + +import ( + "crypto/rand" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/safemem" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" +) + +type randomDevice struct { + ramfs.Entry +} + +func newRandomDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *randomDevice { + r := &randomDevice{} + r.InitEntry(ctx, owner, fs.FilePermsFromMode(mode)) + return r +} + +// DeprecatedPreadv reads random data. +func (*randomDevice) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) { + return dst.CopyOutFrom(ctx, safemem.FromIOReader{rand.Reader}) +} + +// DeprecatedPwritev implements fs.HandleOperations.DeprecatedPwritev. +func (*randomDevice) DeprecatedPwritev(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) { + // On Linux, "Writing to /dev/random or /dev/urandom will update the + // entropy pool with the data written, but this will not result in a higher + // entropy count" - random(4). We don't need to support this, but we do + // need to support the write, so just make it a no-op a la /dev/null. + return src.NumBytes(), nil +} + +// Truncate should be simply ignored for character devices on linux. +func (r *randomDevice) Truncate(context.Context, *fs.Inode, int64) error { + return nil +} |