github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/proc/proc.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 proc implements a partial in-memory file system for profs. 16 package proc 17 18 import ( 19 "fmt" 20 "sort" 21 "strconv" 22 23 "github.com/SagerNet/gvisor/pkg/context" 24 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 25 "github.com/SagerNet/gvisor/pkg/sentry/fs" 26 "github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil" 27 "github.com/SagerNet/gvisor/pkg/sentry/fs/proc/device" 28 "github.com/SagerNet/gvisor/pkg/sentry/fs/proc/seqfile" 29 "github.com/SagerNet/gvisor/pkg/sentry/fs/ramfs" 30 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 31 "github.com/SagerNet/gvisor/pkg/syserror" 32 ) 33 34 // LINT.IfChange 35 36 // proc is a root proc node. 37 // 38 // +stateify savable 39 type proc struct { 40 ramfs.Dir 41 42 // k is the Kernel containing this proc node. 43 k *kernel.Kernel 44 45 // pidns is the PID namespace of the task that mounted the proc filesystem 46 // that this node represents. 47 pidns *kernel.PIDNamespace 48 49 // cgroupControllers is a map of controller name to directory in the 50 // cgroup hierarchy. These controllers are immutable and will be listed 51 // in /proc/pid/cgroup if not nil. 52 cgroupControllers map[string]string 53 } 54 55 // New returns the root node of a partial simple procfs. 56 func New(ctx context.Context, msrc *fs.MountSource, cgroupControllers map[string]string) (*fs.Inode, error) { 57 k := kernel.KernelFromContext(ctx) 58 if k == nil { 59 return nil, fmt.Errorf("procfs requires a kernel") 60 } 61 pidns := kernel.PIDNamespaceFromContext(ctx) 62 if pidns == nil { 63 return nil, fmt.Errorf("procfs requires a PID namespace") 64 } 65 66 // Note that these are just the static members. There are dynamic 67 // members populated in Readdir and Lookup below. 68 contents := map[string]*fs.Inode{ 69 "cpuinfo": newCPUInfo(ctx, msrc), 70 "filesystems": seqfile.NewSeqFileInode(ctx, &filesystemsData{}, msrc), 71 "loadavg": seqfile.NewSeqFileInode(ctx, &loadavgData{}, msrc), 72 "meminfo": seqfile.NewSeqFileInode(ctx, &meminfoData{k}, msrc), 73 "mounts": newProcInode(ctx, ramfs.NewSymlink(ctx, fs.RootOwner, "self/mounts"), msrc, fs.Symlink, nil), 74 "net": newProcInode(ctx, ramfs.NewSymlink(ctx, fs.RootOwner, "self/net"), msrc, fs.Symlink, nil), 75 "self": newSelf(ctx, pidns, msrc), 76 "stat": seqfile.NewSeqFileInode(ctx, &statData{k}, msrc), 77 "thread-self": newThreadSelf(ctx, pidns, msrc), 78 "uptime": newUptime(ctx, msrc), 79 "version": seqfile.NewSeqFileInode(ctx, &versionData{k}, msrc), 80 } 81 82 // Construct the proc InodeOperations. 83 p := &proc{ 84 Dir: *ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555)), 85 k: k, 86 pidns: pidns, 87 cgroupControllers: cgroupControllers, 88 } 89 90 // Add more contents that need proc to be initialized. 91 p.AddChild(ctx, "sys", p.newSysDir(ctx, msrc)) 92 93 return newProcInode(ctx, p, msrc, fs.SpecialDirectory, nil), nil 94 } 95 96 // self is a magical link. 97 // 98 // +stateify savable 99 type self struct { 100 ramfs.Symlink 101 102 pidns *kernel.PIDNamespace 103 } 104 105 // newSelf returns a new "self" node. 106 func newSelf(ctx context.Context, pidns *kernel.PIDNamespace, msrc *fs.MountSource) *fs.Inode { 107 s := &self{ 108 Symlink: *ramfs.NewSymlink(ctx, fs.RootOwner, ""), 109 pidns: pidns, 110 } 111 return newProcInode(ctx, s, msrc, fs.Symlink, nil) 112 } 113 114 // newThreadSelf returns a new "threadSelf" node. 115 func newThreadSelf(ctx context.Context, pidns *kernel.PIDNamespace, msrc *fs.MountSource) *fs.Inode { 116 s := &threadSelf{ 117 Symlink: *ramfs.NewSymlink(ctx, fs.RootOwner, ""), 118 pidns: pidns, 119 } 120 return newProcInode(ctx, s, msrc, fs.Symlink, nil) 121 } 122 123 // Readlink implements fs.InodeOperations.Readlink. 124 func (s *self) Readlink(ctx context.Context, inode *fs.Inode) (string, error) { 125 if t := kernel.TaskFromContext(ctx); t != nil { 126 tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup()) 127 if tgid == 0 { 128 return "", syserror.ENOENT 129 } 130 return strconv.FormatUint(uint64(tgid), 10), nil 131 } 132 133 // Who is reading this link? 134 return "", linuxerr.EINVAL 135 } 136 137 // threadSelf is more magical than "self" link. 138 // 139 // +stateify savable 140 type threadSelf struct { 141 ramfs.Symlink 142 143 pidns *kernel.PIDNamespace 144 } 145 146 // Readlink implements fs.InodeOperations.Readlink. 147 func (s *threadSelf) Readlink(ctx context.Context, inode *fs.Inode) (string, error) { 148 if t := kernel.TaskFromContext(ctx); t != nil { 149 tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup()) 150 tid := s.pidns.IDOfTask(t) 151 if tid == 0 || tgid == 0 { 152 return "", syserror.ENOENT 153 } 154 return fmt.Sprintf("%d/task/%d", tgid, tid), nil 155 } 156 157 // Who is reading this link? 158 return "", linuxerr.EINVAL 159 } 160 161 // Lookup loads an Inode at name into a Dirent. 162 func (p *proc) Lookup(ctx context.Context, dir *fs.Inode, name string) (*fs.Dirent, error) { 163 dirent, walkErr := p.Dir.Lookup(ctx, dir, name) 164 if walkErr == nil { 165 return dirent, nil 166 } 167 168 // Try to lookup a corresponding task. 169 tid, err := strconv.ParseUint(name, 10, 64) 170 if err != nil { 171 // Ignore the parse error and return the original. 172 return nil, walkErr 173 } 174 175 // Grab the other task. 176 otherTask := p.pidns.TaskWithID(kernel.ThreadID(tid)) 177 if otherTask == nil { 178 // Per above. 179 return nil, walkErr 180 } 181 182 // Wrap it in a taskDir. 183 td := p.newTaskDir(ctx, otherTask, dir.MountSource, true) 184 return fs.NewDirent(ctx, td, name), nil 185 } 186 187 // GetFile implements fs.InodeOperations. 188 func (p *proc) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 189 return fs.NewFile(ctx, dirent, flags, &rootProcFile{iops: p}), nil 190 } 191 192 // rootProcFile implements fs.FileOperations for the proc directory. 193 // 194 // +stateify savable 195 type rootProcFile struct { 196 fsutil.DirFileOperations `state:"nosave"` 197 fsutil.FileUseInodeUnstableAttr `state:"nosave"` 198 199 iops *proc 200 } 201 202 var _ fs.FileOperations = (*rootProcFile)(nil) 203 204 // Readdir implements fs.FileOperations.Readdir. 205 func (rpf *rootProcFile) Readdir(ctx context.Context, file *fs.File, ser fs.DentrySerializer) (int64, error) { 206 offset := file.Offset() 207 dirCtx := &fs.DirCtx{ 208 Serializer: ser, 209 } 210 211 // Get normal directory contents from ramfs dir. 212 names, m := rpf.iops.Dir.Children() 213 214 // Add dot and dotdot. 215 root := fs.RootFromContext(ctx) 216 if root != nil { 217 defer root.DecRef(ctx) 218 } 219 dot, dotdot := file.Dirent.GetDotAttrs(root) 220 names = append(names, ".", "..") 221 m["."] = dot 222 m[".."] = dotdot 223 224 // Collect tasks. 225 // Per linux we only include it in directory listings if it's the leader. 226 // But for whatever crazy reason, you can still walk to the given node. 227 for _, tg := range rpf.iops.pidns.ThreadGroups() { 228 if leader := tg.Leader(); leader != nil { 229 name := strconv.FormatUint(uint64(rpf.iops.pidns.IDOfThreadGroup(tg)), 10) 230 m[name] = fs.GenericDentAttr(fs.SpecialDirectory, device.ProcDevice) 231 names = append(names, name) 232 } 233 } 234 235 if offset >= int64(len(m)) { 236 return offset, nil 237 } 238 sort.Strings(names) 239 names = names[offset:] 240 for _, name := range names { 241 if err := dirCtx.DirEmit(name, m[name]); err != nil { 242 return offset, err 243 } 244 offset++ 245 } 246 return offset, nil 247 } 248 249 // LINT.ThenChange(../../fsimpl/proc/tasks.go)