github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/devtmpfs/devtmpfs_test.go (about) 1 // Copyright 2020 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 devtmpfs 16 17 import ( 18 "path" 19 "testing" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/fspath" 24 "github.com/SagerNet/gvisor/pkg/sentry/contexttest" 25 "github.com/SagerNet/gvisor/pkg/sentry/fsimpl/tmpfs" 26 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 27 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 28 ) 29 30 const devPath = "/dev" 31 32 func setupDevtmpfs(t *testing.T) (context.Context, *auth.Credentials, *vfs.VirtualFilesystem, vfs.VirtualDentry, func()) { 33 t.Helper() 34 35 ctx := contexttest.Context(t) 36 creds := auth.CredentialsFromContext(ctx) 37 vfsObj := &vfs.VirtualFilesystem{} 38 if err := vfsObj.Init(ctx); err != nil { 39 t.Fatalf("VFS init: %v", err) 40 } 41 // Register tmpfs just so that we can have a root filesystem that isn't 42 // devtmpfs. 43 vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ 44 AllowUserMount: true, 45 }) 46 vfsObj.MustRegisterFilesystemType("devtmpfs", &FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ 47 AllowUserMount: true, 48 }) 49 50 // Create a test mount namespace with devtmpfs mounted at "/dev". 51 mntns, err := vfsObj.NewMountNamespace(ctx, creds, "tmpfs" /* source */, "tmpfs" /* fsTypeName */, &vfs.MountOptions{}) 52 if err != nil { 53 t.Fatalf("failed to create tmpfs root mount: %v", err) 54 } 55 root := mntns.Root() 56 root.IncRef() 57 devpop := vfs.PathOperation{ 58 Root: root, 59 Start: root, 60 Path: fspath.Parse(devPath), 61 } 62 if err := vfsObj.MkdirAt(ctx, creds, &devpop, &vfs.MkdirOptions{ 63 Mode: 0755, 64 }); err != nil { 65 t.Fatalf("failed to create mount point: %v", err) 66 } 67 if _, err := vfsObj.MountAt(ctx, creds, "devtmpfs" /* source */, &devpop, "devtmpfs" /* fsTypeName */, &vfs.MountOptions{}); err != nil { 68 t.Fatalf("failed to mount devtmpfs: %v", err) 69 } 70 71 return ctx, creds, vfsObj, root, func() { 72 root.DecRef(ctx) 73 mntns.DecRef(ctx) 74 } 75 } 76 77 func TestUserspaceInit(t *testing.T) { 78 ctx, creds, vfsObj, root, cleanup := setupDevtmpfs(t) 79 defer cleanup() 80 81 a, err := NewAccessor(ctx, vfsObj, creds, "devtmpfs") 82 if err != nil { 83 t.Fatalf("failed to create devtmpfs.Accessor: %v", err) 84 } 85 defer a.Release(ctx) 86 87 // Create "userspace-initialized" files using a devtmpfs.Accessor. 88 if err := a.UserspaceInit(ctx); err != nil { 89 t.Fatalf("failed to userspace-initialize devtmpfs: %v", err) 90 } 91 92 // Created files should be visible in the test mount namespace. 93 links := []struct { 94 source string 95 target string 96 }{ 97 { 98 source: "fd", 99 target: "/proc/self/fd", 100 }, 101 { 102 source: "stdin", 103 target: "/proc/self/fd/0", 104 }, 105 { 106 source: "stdout", 107 target: "/proc/self/fd/1", 108 }, 109 { 110 source: "stderr", 111 target: "/proc/self/fd/2", 112 }, 113 { 114 source: "ptmx", 115 target: "pts/ptmx", 116 }, 117 } 118 119 for _, link := range links { 120 abspath := path.Join(devPath, link.source) 121 if gotTarget, err := vfsObj.ReadlinkAt(ctx, creds, &vfs.PathOperation{ 122 Root: root, 123 Start: root, 124 Path: fspath.Parse(abspath), 125 }); err != nil || gotTarget != link.target { 126 t.Errorf("readlink(%q): got (%q, %v), wanted (%q, nil)", abspath, gotTarget, err, link.target) 127 } 128 } 129 130 dirs := []string{"shm", "pts"} 131 for _, dir := range dirs { 132 abspath := path.Join(devPath, dir) 133 statx, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{ 134 Root: root, 135 Start: root, 136 Path: fspath.Parse(abspath), 137 }, &vfs.StatOptions{ 138 Mask: linux.STATX_MODE, 139 }) 140 if err != nil { 141 t.Errorf("stat(%q): got error %v ", abspath, err) 142 continue 143 } 144 if want := uint16(0755) | linux.S_IFDIR; statx.Mode != want { 145 t.Errorf("stat(%q): got mode %x, want %x", abspath, statx.Mode, want) 146 } 147 } 148 } 149 150 func TestCreateDeviceFile(t *testing.T) { 151 ctx, creds, vfsObj, root, cleanup := setupDevtmpfs(t) 152 defer cleanup() 153 154 a, err := NewAccessor(ctx, vfsObj, creds, "devtmpfs") 155 if err != nil { 156 t.Fatalf("failed to create devtmpfs.Accessor: %v", err) 157 } 158 defer a.Release(ctx) 159 160 devFiles := []struct { 161 path string 162 kind vfs.DeviceKind 163 major uint32 164 minor uint32 165 perms uint16 166 }{ 167 { 168 path: "dummy", 169 kind: vfs.CharDevice, 170 major: 12, 171 minor: 34, 172 perms: 0600, 173 }, 174 { 175 path: "foo/bar", 176 kind: vfs.BlockDevice, 177 major: 13, 178 minor: 35, 179 perms: 0660, 180 }, 181 { 182 path: "foo/baz", 183 kind: vfs.CharDevice, 184 major: 12, 185 minor: 40, 186 perms: 0666, 187 }, 188 { 189 path: "a/b/c/d/e", 190 kind: vfs.BlockDevice, 191 major: 12, 192 minor: 34, 193 perms: 0600, 194 }, 195 } 196 197 for _, f := range devFiles { 198 if err := a.CreateDeviceFile(ctx, f.path, f.kind, f.major, f.minor, f.perms); err != nil { 199 t.Fatalf("failed to create device file: %v", err) 200 } 201 // The device special file should be visible in the test mount namespace. 202 abspath := path.Join(devPath, f.path) 203 stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{ 204 Root: root, 205 Start: root, 206 Path: fspath.Parse(abspath), 207 }, &vfs.StatOptions{ 208 Mask: linux.STATX_TYPE | linux.STATX_MODE, 209 }) 210 if err != nil { 211 t.Fatalf("failed to stat device file at %q: %v", abspath, err) 212 } 213 if stat.RdevMajor != f.major { 214 t.Errorf("major device number: got %v, wanted %v", stat.RdevMajor, f.major) 215 } 216 if stat.RdevMinor != f.minor { 217 t.Errorf("minor device number: got %v, wanted %v", stat.RdevMinor, f.minor) 218 } 219 wantMode := f.perms 220 switch f.kind { 221 case vfs.CharDevice: 222 wantMode |= linux.S_IFCHR 223 case vfs.BlockDevice: 224 wantMode |= linux.S_IFBLK 225 } 226 if stat.Mode != wantMode { 227 t.Errorf("device file mode: got %v, wanted %v", stat.Mode, wantMode) 228 } 229 } 230 }