github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/fsimpl/devpts/devpts.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 devpts provides a filesystem implementation that behaves like 16 // devpts. 17 package devpts 18 19 import ( 20 "fmt" 21 "math" 22 "sort" 23 "strconv" 24 "sync" 25 26 "github.com/MerlinKodo/gvisor/pkg/abi/linux" 27 "github.com/MerlinKodo/gvisor/pkg/context" 28 "github.com/MerlinKodo/gvisor/pkg/errors/linuxerr" 29 "github.com/MerlinKodo/gvisor/pkg/sentry/fsimpl/kernfs" 30 "github.com/MerlinKodo/gvisor/pkg/sentry/kernel/auth" 31 "github.com/MerlinKodo/gvisor/pkg/sentry/vfs" 32 ) 33 34 // Name is the filesystem name. 35 const Name = "devpts" 36 37 // FilesystemType implements vfs.FilesystemType. 38 // 39 // +stateify savable 40 type FilesystemType struct { 41 initOnce sync.Once `state:"nosave"` // FIXME(gvisor.dev/issue/1663): not yet supported. 42 initErr error 43 44 // fs backs all mounts of this FilesystemType. root is fs' root. fs and root 45 // are immutable. 46 fs *vfs.Filesystem 47 root *vfs.Dentry 48 } 49 50 // Name implements vfs.FilesystemType.Name. 51 func (*FilesystemType) Name() string { 52 return Name 53 } 54 55 // GetFilesystem implements vfs.FilesystemType.GetFilesystem. 56 func (fstype *FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) { 57 // No data allowed. 58 if opts.Data != "" { 59 return nil, nil, linuxerr.EINVAL 60 } 61 62 fstype.initOnce.Do(func() { 63 fs, root, err := fstype.newFilesystem(ctx, vfsObj, creds) 64 if err != nil { 65 fstype.initErr = err 66 return 67 } 68 fstype.fs = fs.VFSFilesystem() 69 fstype.root = root.VFSDentry() 70 }) 71 if fstype.initErr != nil { 72 return nil, nil, fstype.initErr 73 } 74 fstype.fs.IncRef() 75 fstype.root.IncRef() 76 return fstype.fs, fstype.root, nil 77 } 78 79 // Release implements vfs.FilesystemType.Release. 80 func (fstype *FilesystemType) Release(ctx context.Context) { 81 if fstype.fs != nil { 82 fstype.root.DecRef(ctx) 83 fstype.fs.DecRef(ctx) 84 } 85 } 86 87 // +stateify savable 88 type filesystem struct { 89 kernfs.Filesystem 90 91 devMinor uint32 92 } 93 94 // newFilesystem creates a new devpts filesystem with root directory and ptmx 95 // master inode. It returns the filesystem and root Dentry. 96 func (fstype *FilesystemType) newFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials) (*filesystem, *kernfs.Dentry, error) { 97 devMinor, err := vfsObj.GetAnonBlockDevMinor() 98 if err != nil { 99 return nil, nil, err 100 } 101 102 fs := &filesystem{ 103 devMinor: devMinor, 104 } 105 fs.Filesystem.VFSFilesystem().Init(vfsObj, fstype, fs) 106 107 // Construct the root directory. This is always inode id 1. 108 root := &rootInode{ 109 replicas: make(map[uint32]*replicaInode), 110 } 111 root.InodeAttrs.Init(ctx, creds, linux.UNNAMED_MAJOR, devMinor, 1, linux.ModeDirectory|0555) 112 root.OrderedChildren.Init(kernfs.OrderedChildrenOptions{}) 113 root.InitRefs() 114 115 var rootD kernfs.Dentry 116 rootD.InitRoot(&fs.Filesystem, root) 117 118 // Construct the pts master inode and dentry. Linux always uses inode 119 // id 2 for ptmx. See fs/devpts/inode.c:mknod_ptmx. 120 master := &masterInode{ 121 root: root, 122 } 123 master.InodeAttrs.Init(ctx, creds, linux.UNNAMED_MAJOR, devMinor, 2, linux.ModeCharacterDevice|0666) 124 125 // Add the master as a child of the root. 126 links := root.OrderedChildren.Populate(map[string]kernfs.Inode{ 127 "ptmx": master, 128 }) 129 root.IncLinks(links) 130 131 return fs, &rootD, nil 132 } 133 134 // Release implements vfs.FilesystemImpl.Release. 135 func (fs *filesystem) Release(ctx context.Context) { 136 fs.Filesystem.VFSFilesystem().VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor) 137 fs.Filesystem.Release(ctx) 138 } 139 140 // MountOptions implements vfs.FilesystemImpl.MountOptions. 141 func (fs *filesystem) MountOptions() string { 142 return "" 143 } 144 145 // rootInode is the root directory inode for the devpts mounts. 146 // 147 // +stateify savable 148 type rootInode struct { 149 implStatFS 150 kernfs.InodeAlwaysValid 151 kernfs.InodeAttrs 152 kernfs.InodeDirectoryNoNewChildren 153 kernfs.InodeNotAnonymous 154 kernfs.InodeNotSymlink 155 kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid. 156 kernfs.InodeWatches 157 kernfs.OrderedChildren 158 rootInodeRefs 159 160 locks vfs.FileLocks 161 162 // master is the master pty inode. Immutable. 163 master *masterInode 164 165 // mu protects the fields below. 166 mu sync.Mutex `state:"nosave"` 167 168 // replicas maps pty ids to replica inodes. 169 replicas map[uint32]*replicaInode 170 171 // nextIdx is the next pty index to use. Must be accessed atomically. 172 // 173 // TODO(b/29356795): reuse indices when ptys are closed. 174 nextIdx uint32 175 } 176 177 var _ kernfs.Inode = (*rootInode)(nil) 178 179 // allocateTerminal creates a new Terminal and installs a pts node for it. 180 func (i *rootInode) allocateTerminal(ctx context.Context, creds *auth.Credentials) (*Terminal, error) { 181 i.mu.Lock() 182 defer i.mu.Unlock() 183 if i.nextIdx == math.MaxUint32 { 184 return nil, linuxerr.ENOMEM 185 } 186 idx := i.nextIdx 187 i.nextIdx++ 188 189 // Sanity check that replica with idx does not exist. 190 if _, ok := i.replicas[idx]; ok { 191 panic(fmt.Sprintf("pty index collision; index %d already exists", idx)) 192 } 193 194 // Create the new terminal and replica. 195 t := newTerminal(idx) 196 replica := &replicaInode{ 197 root: i, 198 t: t, 199 } 200 // Linux always uses pty index + 3 as the inode id. See 201 // fs/devpts/inode.c:devpts_pty_new(). 202 replica.InodeAttrs.Init(ctx, creds, i.InodeAttrs.DevMajor(), i.InodeAttrs.DevMinor(), uint64(idx+3), linux.ModeCharacterDevice|0600) 203 i.replicas[idx] = replica 204 205 return t, nil 206 } 207 208 // masterClose is called when the master end of t is closed. 209 func (i *rootInode) masterClose(ctx context.Context, t *Terminal) { 210 i.mu.Lock() 211 defer i.mu.Unlock() 212 213 // Sanity check that replica with idx exists. 214 ri, ok := i.replicas[t.n] 215 if !ok { 216 panic(fmt.Sprintf("pty with index %d does not exist", t.n)) 217 } 218 219 // Drop the ref on replica inode taken during rootInode.allocateTerminal. 220 ri.DecRef(ctx) 221 delete(i.replicas, t.n) 222 } 223 224 // Open implements kernfs.Inode.Open. 225 func (i *rootInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 226 opts.Flags &= linux.O_ACCMODE | linux.O_CREAT | linux.O_EXCL | linux.O_TRUNC | 227 linux.O_DIRECTORY | linux.O_NOFOLLOW | linux.O_NONBLOCK | linux.O_NOCTTY 228 fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), d, &i.OrderedChildren, &i.locks, &opts, kernfs.GenericDirectoryFDOptions{ 229 SeekEnd: kernfs.SeekEndStaticEntries, 230 }) 231 if err != nil { 232 return nil, err 233 } 234 return fd.VFSFileDescription(), nil 235 } 236 237 // Lookup implements kernfs.Inode.Lookup. 238 func (i *rootInode) Lookup(ctx context.Context, name string) (kernfs.Inode, error) { 239 // Check if a static entry was looked up. 240 if d, err := i.OrderedChildren.Lookup(ctx, name); err == nil { 241 return d, nil 242 } 243 244 // Not a static entry. 245 idx, err := strconv.ParseUint(name, 10, 32) 246 if err != nil { 247 return nil, linuxerr.ENOENT 248 } 249 i.mu.Lock() 250 defer i.mu.Unlock() 251 if ri, ok := i.replicas[uint32(idx)]; ok { 252 ri.IncRef() // This ref is passed to the dentry upon creation via Init. 253 return ri, nil 254 255 } 256 return nil, linuxerr.ENOENT 257 } 258 259 // IterDirents implements kernfs.Inode.IterDirents. 260 func (i *rootInode) IterDirents(ctx context.Context, mnt *vfs.Mount, cb vfs.IterDirentsCallback, offset, relOffset int64) (int64, error) { 261 i.mu.Lock() 262 defer i.mu.Unlock() 263 i.InodeAttrs.TouchAtime(ctx, mnt) 264 ids := make([]int, 0, len(i.replicas)) 265 for id := range i.replicas { 266 ids = append(ids, int(id)) 267 } 268 sort.Ints(ids) 269 for _, id := range ids[relOffset:] { 270 dirent := vfs.Dirent{ 271 Name: strconv.FormatUint(uint64(id), 10), 272 Type: linux.DT_CHR, 273 Ino: i.replicas[uint32(id)].InodeAttrs.Ino(), 274 NextOff: offset + 1, 275 } 276 if err := cb.Handle(dirent); err != nil { 277 return offset, err 278 } 279 offset++ 280 } 281 return offset, nil 282 } 283 284 // DecRef implements kernfs.Inode.DecRef. 285 func (i *rootInode) DecRef(ctx context.Context) { 286 i.rootInodeRefs.DecRef(func() { i.Destroy(ctx) }) 287 } 288 289 // +stateify savable 290 type implStatFS struct{} 291 292 // StatFS implements kernfs.Inode.StatFS. 293 func (*implStatFS) StatFS(context.Context, *vfs.Filesystem) (linux.Statfs, error) { 294 return vfs.GenericStatFS(linux.DEVPTS_SUPER_MAGIC), nil 295 }