2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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 (
|
2019-01-15 04:33:29 +00:00
|
|
|
"math"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs/ramfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs/tmpfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
device: fix device major/minor
Current gvisor doesn't give devices a right major and minor number.
When testing golang supporting of gvisor, I run the test case below:
```
$ docker run -ti --runtime runsc golang:1.12.1 bash -c "cd /usr/local/go/src && ./run.bash "
```
And it reports some errors, one of them is:
"--- FAIL: TestDevices (0.00s)
--- FAIL: TestDevices//dev/null_1:3 (0.00s)
dev_linux_test.go:45: for /dev/null Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/null Minor(0x0) == 0, want 3
dev_linux_test.go:51: for /dev/null Mkdev(1, 3) == 0x103, want 0x0
--- FAIL: TestDevices//dev/zero_1:5 (0.00s)
dev_linux_test.go:45: for /dev/zero Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/zero Minor(0x0) == 0, want 5
dev_linux_test.go:51: for /dev/zero Mkdev(1, 5) == 0x105, want 0x0
--- FAIL: TestDevices//dev/random_1:8 (0.00s)
dev_linux_test.go:45: for /dev/random Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/random Minor(0x0) == 0, want 8
dev_linux_test.go:51: for /dev/random Mkdev(1, 8) == 0x108, want 0x0
--- FAIL: TestDevices//dev/full_1:7 (0.00s)
dev_linux_test.go:45: for /dev/full Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/full Minor(0x0) == 0, want 7
dev_linux_test.go:51: for /dev/full Mkdev(1, 7) == 0x107, want 0x0
--- FAIL: TestDevices//dev/urandom_1:9 (0.00s)
dev_linux_test.go:45: for /dev/urandom Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/urandom Minor(0x0) == 0, want 9
dev_linux_test.go:51: for /dev/urandom Mkdev(1, 9) == 0x109, want 0x0
"
So I think we'd better assign to them correct major/minor numbers following linux spec.
Signed-off-by: Wei Zhang <zhangwei198900@gmail.com>
Change-Id: I4521ee7884b4e214fd3a261929e3b6dac537ada9
PiperOrigin-RevId: 241609021
2019-04-02 21:50:09 +00:00
|
|
|
// Memory device numbers are from Linux's drivers/char/mem.c
|
|
|
|
const (
|
|
|
|
// Mem device major.
|
|
|
|
memDevMajor uint16 = 1
|
|
|
|
|
|
|
|
// Mem device minors.
|
|
|
|
nullDevMinor uint32 = 3
|
|
|
|
zeroDevMinor uint32 = 5
|
|
|
|
fullDevMinor uint32 = 7
|
|
|
|
randomDevMinor uint32 = 8
|
|
|
|
urandomDevMinor uint32 = 9
|
|
|
|
)
|
|
|
|
|
2019-07-12 04:24:27 +00:00
|
|
|
// TTY major device number comes from include/uapi/linux/major.h.
|
|
|
|
const (
|
|
|
|
ttyDevMinor = 0
|
|
|
|
ttyDevMajor = 5
|
|
|
|
)
|
|
|
|
|
|
|
|
func newCharacterDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, major uint16, minor uint32) *fs.Inode {
|
2019-06-14 01:39:43 +00:00
|
|
|
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
|
2019-07-12 04:24:27 +00:00
|
|
|
DeviceID: devDevice.DeviceID(),
|
|
|
|
InodeID: devDevice.NextIno(),
|
|
|
|
BlockSize: usermem.PageSize,
|
|
|
|
Type: fs.CharacterDevice,
|
|
|
|
DeviceFileMajor: major,
|
|
|
|
DeviceFileMinor: minor,
|
2018-04-27 17:37:02 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-06-14 01:39:43 +00:00
|
|
|
func newMemDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, minor uint32) *fs.Inode {
|
|
|
|
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
|
device: fix device major/minor
Current gvisor doesn't give devices a right major and minor number.
When testing golang supporting of gvisor, I run the test case below:
```
$ docker run -ti --runtime runsc golang:1.12.1 bash -c "cd /usr/local/go/src && ./run.bash "
```
And it reports some errors, one of them is:
"--- FAIL: TestDevices (0.00s)
--- FAIL: TestDevices//dev/null_1:3 (0.00s)
dev_linux_test.go:45: for /dev/null Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/null Minor(0x0) == 0, want 3
dev_linux_test.go:51: for /dev/null Mkdev(1, 3) == 0x103, want 0x0
--- FAIL: TestDevices//dev/zero_1:5 (0.00s)
dev_linux_test.go:45: for /dev/zero Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/zero Minor(0x0) == 0, want 5
dev_linux_test.go:51: for /dev/zero Mkdev(1, 5) == 0x105, want 0x0
--- FAIL: TestDevices//dev/random_1:8 (0.00s)
dev_linux_test.go:45: for /dev/random Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/random Minor(0x0) == 0, want 8
dev_linux_test.go:51: for /dev/random Mkdev(1, 8) == 0x108, want 0x0
--- FAIL: TestDevices//dev/full_1:7 (0.00s)
dev_linux_test.go:45: for /dev/full Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/full Minor(0x0) == 0, want 7
dev_linux_test.go:51: for /dev/full Mkdev(1, 7) == 0x107, want 0x0
--- FAIL: TestDevices//dev/urandom_1:9 (0.00s)
dev_linux_test.go:45: for /dev/urandom Major(0x0) == 0, want 1
dev_linux_test.go:48: for /dev/urandom Minor(0x0) == 0, want 9
dev_linux_test.go:51: for /dev/urandom Mkdev(1, 9) == 0x109, want 0x0
"
So I think we'd better assign to them correct major/minor numbers following linux spec.
Signed-off-by: Wei Zhang <zhangwei198900@gmail.com>
Change-Id: I4521ee7884b4e214fd3a261929e3b6dac537ada9
PiperOrigin-RevId: 241609021
2019-04-02 21:50:09 +00:00
|
|
|
DeviceID: devDevice.DeviceID(),
|
|
|
|
InodeID: devDevice.NextIno(),
|
|
|
|
BlockSize: usermem.PageSize,
|
|
|
|
Type: fs.CharacterDevice,
|
|
|
|
DeviceFileMajor: memDevMajor,
|
|
|
|
DeviceFileMinor: minor,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
func newDirectory(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
|
2019-01-15 04:33:29 +00:00
|
|
|
iops := ramfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0555))
|
2019-06-14 01:39:43 +00:00
|
|
|
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
|
2018-04-27 17:37:02 +00:00
|
|
|
DeviceID: devDevice.DeviceID(),
|
|
|
|
InodeID: devDevice.NextIno(),
|
|
|
|
BlockSize: usermem.PageSize,
|
|
|
|
Type: fs.Directory,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func newSymlink(ctx context.Context, target string, msrc *fs.MountSource) *fs.Inode {
|
2019-01-15 04:33:29 +00:00
|
|
|
iops := ramfs.NewSymlink(ctx, fs.RootOwner, target)
|
2019-06-14 01:39:43 +00:00
|
|
|
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
|
2018-04-27 17:37:02 +00:00
|
|
|
DeviceID: devDevice.DeviceID(),
|
|
|
|
InodeID: devDevice.NextIno(),
|
|
|
|
BlockSize: usermem.PageSize,
|
|
|
|
Type: fs.Symlink,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// New returns the root node of a device filesystem.
|
2019-06-29 00:19:04 +00:00
|
|
|
func New(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
|
2018-04-27 17:37:02 +00:00
|
|
|
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),
|
|
|
|
|
2019-06-14 01:39:43 +00:00
|
|
|
"null": newMemDevice(ctx, newNullDevice(ctx, fs.RootOwner, 0666), msrc, nullDevMinor),
|
|
|
|
"zero": newMemDevice(ctx, newZeroDevice(ctx, fs.RootOwner, 0666), msrc, zeroDevMinor),
|
|
|
|
"full": newMemDevice(ctx, newFullDevice(ctx, fs.RootOwner, 0666), msrc, fullDevMinor),
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// 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.
|
2019-06-14 01:39:43 +00:00
|
|
|
"random": newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, randomDevMinor),
|
|
|
|
"urandom": newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, urandomDevMinor),
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-01-25 01:01:20 +00:00
|
|
|
"shm": tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc),
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// 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),
|
2019-07-12 04:24:27 +00:00
|
|
|
|
|
|
|
"tty": newCharacterDevice(ctx, newTTYDevice(ctx, fs.RootOwner, 0666), msrc, ttyDevMajor, ttyDevMinor),
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
iops := ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555))
|
2019-06-14 01:39:43 +00:00
|
|
|
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
|
2018-04-27 17:37:02 +00:00
|
|
|
DeviceID: devDevice.DeviceID(),
|
|
|
|
InodeID: devDevice.NextIno(),
|
|
|
|
BlockSize: usermem.PageSize,
|
|
|
|
Type: fs.Directory,
|
|
|
|
})
|
|
|
|
}
|
2019-01-15 04:33:29 +00:00
|
|
|
|
|
|
|
// readZeros implements fs.FileOperations.Read with infinite null bytes.
|
|
|
|
type readZeros struct{}
|
|
|
|
|
|
|
|
// Read implements fs.FileOperations.Read.
|
2019-03-27 18:07:41 +00:00
|
|
|
func (*readZeros) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
|
2019-01-15 04:33:29 +00:00
|
|
|
return dst.ZeroOut(ctx, math.MaxInt64)
|
|
|
|
}
|