github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/dev/dev.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package dev provides a filesystem with simple devices. 16 package dev 17 18 import ( 19 "fmt" 20 "math" 21 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/hostarch" 24 "github.com/SagerNet/gvisor/pkg/sentry/fs" 25 "github.com/SagerNet/gvisor/pkg/sentry/fs/ramfs" 26 "github.com/SagerNet/gvisor/pkg/sentry/fs/tmpfs" 27 "github.com/SagerNet/gvisor/pkg/sentry/inet" 28 "github.com/SagerNet/gvisor/pkg/usermem" 29 ) 30 31 // Memory device numbers are from Linux's drivers/char/mem.c 32 const ( 33 // Mem device major. 34 memDevMajor uint16 = 1 35 36 // Mem device minors. 37 nullDevMinor uint32 = 3 38 zeroDevMinor uint32 = 5 39 fullDevMinor uint32 = 7 40 randomDevMinor uint32 = 8 41 urandomDevMinor uint32 = 9 42 ) 43 44 // TTY major device number comes from include/uapi/linux/major.h. 45 const ( 46 ttyDevMinor = 0 47 ttyDevMajor = 5 48 ) 49 50 func newCharacterDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, major uint16, minor uint32) *fs.Inode { 51 return fs.NewInode(ctx, iops, msrc, fs.StableAttr{ 52 DeviceID: devDevice.DeviceID(), 53 InodeID: devDevice.NextIno(), 54 BlockSize: hostarch.PageSize, 55 Type: fs.CharacterDevice, 56 DeviceFileMajor: major, 57 DeviceFileMinor: minor, 58 }) 59 } 60 61 func newMemDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, minor uint32) *fs.Inode { 62 return fs.NewInode(ctx, iops, msrc, fs.StableAttr{ 63 DeviceID: devDevice.DeviceID(), 64 InodeID: devDevice.NextIno(), 65 BlockSize: hostarch.PageSize, 66 Type: fs.CharacterDevice, 67 DeviceFileMajor: memDevMajor, 68 DeviceFileMinor: minor, 69 }) 70 } 71 72 func newDirectory(ctx context.Context, contents map[string]*fs.Inode, msrc *fs.MountSource) *fs.Inode { 73 iops := ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555)) 74 return fs.NewInode(ctx, iops, msrc, fs.StableAttr{ 75 DeviceID: devDevice.DeviceID(), 76 InodeID: devDevice.NextIno(), 77 BlockSize: hostarch.PageSize, 78 Type: fs.Directory, 79 }) 80 } 81 82 func newSymlink(ctx context.Context, target string, msrc *fs.MountSource) *fs.Inode { 83 iops := ramfs.NewSymlink(ctx, fs.RootOwner, target) 84 return fs.NewInode(ctx, iops, msrc, fs.StableAttr{ 85 DeviceID: devDevice.DeviceID(), 86 InodeID: devDevice.NextIno(), 87 BlockSize: hostarch.PageSize, 88 Type: fs.Symlink, 89 }) 90 } 91 92 // New returns the root node of a device filesystem. 93 func New(ctx context.Context, msrc *fs.MountSource) *fs.Inode { 94 shm, err := tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, nil /* parent */) 95 if err != nil { 96 panic(fmt.Sprintf("tmpfs.NewDir failed: %v", err)) 97 } 98 99 contents := map[string]*fs.Inode{ 100 "fd": newSymlink(ctx, "/proc/self/fd", msrc), 101 "stdin": newSymlink(ctx, "/proc/self/fd/0", msrc), 102 "stdout": newSymlink(ctx, "/proc/self/fd/1", msrc), 103 "stderr": newSymlink(ctx, "/proc/self/fd/2", msrc), 104 105 "null": newMemDevice(ctx, newNullDevice(ctx, fs.RootOwner, 0666), msrc, nullDevMinor), 106 "zero": newMemDevice(ctx, newZeroDevice(ctx, fs.RootOwner, 0666), msrc, zeroDevMinor), 107 "full": newMemDevice(ctx, newFullDevice(ctx, fs.RootOwner, 0666), msrc, fullDevMinor), 108 109 // This is not as good as /dev/random in linux because go 110 // runtime uses sys_random and /dev/urandom internally. 111 // According to 'man 4 random', this will be sufficient unless 112 // application uses this to generate long-lived GPG/SSL/SSH 113 // keys. 114 "random": newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, randomDevMinor), 115 "urandom": newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, urandomDevMinor), 116 117 "shm": shm, 118 119 // A devpts is typically mounted at /dev/pts to provide 120 // pseudoterminal support. Place an empty directory there for 121 // the devpts to be mounted over. 122 "pts": newDirectory(ctx, nil, msrc), 123 // Similarly, applications expect a ptmx device at /dev/ptmx 124 // connected to the terminals provided by /dev/pts/. Rather 125 // than creating a device directly (which requires a hairy 126 // lookup on open to determine if a devpts exists), just create 127 // a symlink to the ptmx provided by devpts. (The Linux devpts 128 // documentation recommends this). 129 // 130 // If no devpts is mounted, this will simply be a dangling 131 // symlink, which is fine. 132 "ptmx": newSymlink(ctx, "pts/ptmx", msrc), 133 134 "tty": newCharacterDevice(ctx, newTTYDevice(ctx, fs.RootOwner, 0666), msrc, ttyDevMajor, ttyDevMinor), 135 } 136 137 if isNetTunSupported(inet.StackFromContext(ctx)) { 138 contents["net"] = newDirectory(ctx, map[string]*fs.Inode{ 139 "tun": newCharacterDevice(ctx, newNetTunDevice(ctx, fs.RootOwner, 0666), msrc, netTunDevMajor, netTunDevMinor), 140 }, msrc) 141 } 142 143 iops := ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555)) 144 return fs.NewInode(ctx, iops, msrc, fs.StableAttr{ 145 DeviceID: devDevice.DeviceID(), 146 InodeID: devDevice.NextIno(), 147 BlockSize: hostarch.PageSize, 148 Type: fs.Directory, 149 }) 150 } 151 152 // readZeros implements fs.FileOperations.Read with infinite null bytes. 153 type readZeros struct{} 154 155 // Read implements fs.FileOperations.Read. 156 func (*readZeros) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) { 157 return dst.ZeroOut(ctx, math.MaxInt64) 158 }