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