github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/proc/fds.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 16 17 import ( 18 "fmt" 19 "sort" 20 "strconv" 21 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/sentry/fs" 24 "github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil" 25 "github.com/SagerNet/gvisor/pkg/sentry/fs/proc/device" 26 "github.com/SagerNet/gvisor/pkg/sentry/fs/ramfs" 27 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 28 "github.com/SagerNet/gvisor/pkg/syserror" 29 ) 30 31 // LINT.IfChange 32 33 // walkDescriptors finds the descriptor (file-flag pair) for the fd identified 34 // by p, and calls the toInodeOperations callback with that descriptor. This is a helper 35 // method for implementing fs.InodeOperations.Lookup. 36 func walkDescriptors(t *kernel.Task, p string, toInode func(*fs.File, kernel.FDFlags) *fs.Inode) (*fs.Inode, error) { 37 n, err := strconv.ParseUint(p, 10, 64) 38 if err != nil { 39 // Not found. 40 return nil, syserror.ENOENT 41 } 42 43 var file *fs.File 44 var fdFlags kernel.FDFlags 45 t.WithMuLocked(func(t *kernel.Task) { 46 if fdTable := t.FDTable(); fdTable != nil { 47 file, fdFlags = fdTable.Get(int32(n)) 48 } 49 }) 50 if file == nil { 51 return nil, syserror.ENOENT 52 } 53 return toInode(file, fdFlags), nil 54 } 55 56 // readDescriptors reads fds in the task starting at offset, and calls the 57 // toDentAttr callback for each to get a DentAttr, which it then emits. This is 58 // a helper for implementing fs.InodeOperations.Readdir. 59 func readDescriptors(ctx context.Context, t *kernel.Task, c *fs.DirCtx, offset int64, toDentAttr func(int) fs.DentAttr) (int64, error) { 60 var fds []int32 61 t.WithMuLocked(func(t *kernel.Task) { 62 if fdTable := t.FDTable(); fdTable != nil { 63 fds = fdTable.GetFDs(ctx) 64 } 65 }) 66 67 // Find the appropriate starting point. 68 idx := sort.Search(len(fds), func(i int) bool { return fds[i] >= int32(offset) }) 69 if idx == len(fds) { 70 return offset, nil 71 } 72 fds = fds[idx:] 73 74 // Serialize all FDs. 75 for _, fd := range fds { 76 name := strconv.FormatUint(uint64(fd), 10) 77 if err := c.DirEmit(name, toDentAttr(int(fd))); err != nil { 78 // Returned offset is the next fd to serialize. 79 return int64(fd), err 80 } 81 } 82 // We serialized them all. Next offset should be higher than last 83 // serialized fd. 84 return int64(fds[len(fds)-1] + 1), nil 85 } 86 87 // fd implements fs.InodeOperations for a file in /proc/TID/fd/. 88 type fd struct { 89 ramfs.Symlink 90 file *fs.File 91 } 92 93 var _ fs.InodeOperations = (*fd)(nil) 94 95 // newFd returns a new fd based on an existing file. 96 // 97 // This inherits one reference to the file. 98 func newFd(ctx context.Context, t *kernel.Task, f *fs.File, msrc *fs.MountSource) *fs.Inode { 99 fd := &fd{ 100 // RootOwner overridden by taskOwnedInodeOps.UnstableAttrs(). 101 Symlink: *ramfs.NewSymlink(ctx, fs.RootOwner, ""), 102 file: f, 103 } 104 return newProcInode(ctx, fd, msrc, fs.Symlink, t) 105 } 106 107 // GetFile returns the fs.File backing this fd. The dirent and flags 108 // arguments are ignored. 109 func (f *fd) GetFile(context.Context, *fs.Dirent, fs.FileFlags) (*fs.File, error) { 110 // Take a reference on the fs.File. 111 f.file.IncRef() 112 return f.file, nil 113 } 114 115 // Readlink returns the current target. 116 func (f *fd) Readlink(ctx context.Context, _ *fs.Inode) (string, error) { 117 root := fs.RootFromContext(ctx) 118 if root != nil { 119 defer root.DecRef(ctx) 120 } 121 n, _ := f.file.Dirent.FullName(root) 122 return n, nil 123 } 124 125 // Getlink implements fs.InodeOperations.Getlink. 126 func (f *fd) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) { 127 f.file.Dirent.IncRef() 128 return f.file.Dirent, nil 129 } 130 131 // Truncate is ignored. 132 func (f *fd) Truncate(context.Context, *fs.Inode, int64) error { 133 return nil 134 } 135 136 func (f *fd) Release(ctx context.Context) { 137 f.Symlink.Release(ctx) 138 f.file.DecRef(ctx) 139 } 140 141 // fdDir is an InodeOperations for /proc/TID/fd. 142 // 143 // +stateify savable 144 type fdDir struct { 145 ramfs.Dir 146 147 // We hold a reference on the task's FDTable but only keep an indirect 148 // task pointer to avoid Dirent loading circularity caused by the 149 // table's back pointers into the dirent tree. 150 t *kernel.Task 151 } 152 153 var _ fs.InodeOperations = (*fdDir)(nil) 154 155 // newFdDir creates a new fdDir. 156 func newFdDir(ctx context.Context, t *kernel.Task, msrc *fs.MountSource) *fs.Inode { 157 f := &fdDir{ 158 Dir: *ramfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermissions{User: fs.PermMask{Read: true, Execute: true}}), 159 t: t, 160 } 161 return newProcInode(ctx, f, msrc, fs.SpecialDirectory, t) 162 } 163 164 // Check implements InodeOperations.Check. 165 // 166 // This is to match Linux, which uses a special permission handler to guarantee 167 // that a process can still access /proc/self/fd after it has executed 168 // setuid. See fs/proc/fd.c:proc_fd_permission. 169 func (f *fdDir) Check(ctx context.Context, inode *fs.Inode, req fs.PermMask) bool { 170 if fs.ContextCanAccessFile(ctx, inode, req) { 171 return true 172 } 173 if t := kernel.TaskFromContext(ctx); t != nil { 174 // Allow access if the task trying to access it is in the 175 // thread group corresponding to this directory. 176 if f.t.ThreadGroup() == t.ThreadGroup() { 177 return true 178 } 179 } 180 return false 181 } 182 183 // Lookup loads an Inode in /proc/TID/fd into a Dirent. 184 func (f *fdDir) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) { 185 n, err := walkDescriptors(f.t, p, func(file *fs.File, _ kernel.FDFlags) *fs.Inode { 186 return newFd(ctx, f.t, file, dir.MountSource) 187 }) 188 if err != nil { 189 return nil, err 190 } 191 return fs.NewDirent(ctx, n, p), nil 192 } 193 194 // GetFile implements fs.FileOperations.GetFile. 195 func (f *fdDir) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 196 fops := &fdDirFile{ 197 isInfoFile: false, 198 t: f.t, 199 } 200 return fs.NewFile(ctx, dirent, flags, fops), nil 201 } 202 203 // +stateify savable 204 type fdDirFile struct { 205 fsutil.DirFileOperations `state:"nosave"` 206 fsutil.FileUseInodeUnstableAttr `state:"nosave"` 207 208 isInfoFile bool 209 210 t *kernel.Task 211 } 212 213 var _ fs.FileOperations = (*fdDirFile)(nil) 214 215 // Readdir implements fs.FileOperations.Readdir. 216 func (f *fdDirFile) Readdir(ctx context.Context, file *fs.File, ser fs.DentrySerializer) (int64, error) { 217 dirCtx := &fs.DirCtx{ 218 Serializer: ser, 219 } 220 typ := fs.RegularFile 221 if f.isInfoFile { 222 typ = fs.Symlink 223 } 224 return readDescriptors(ctx, f.t, dirCtx, file.Offset(), func(fd int) fs.DentAttr { 225 return fs.GenericDentAttr(typ, device.ProcDevice) 226 }) 227 } 228 229 // fdInfoDir implements /proc/TID/fdinfo. It embeds an fdDir, but overrides 230 // Lookup and Readdir. 231 // 232 // +stateify savable 233 type fdInfoDir struct { 234 ramfs.Dir 235 236 t *kernel.Task 237 } 238 239 // newFdInfoDir creates a new fdInfoDir. 240 func newFdInfoDir(ctx context.Context, t *kernel.Task, msrc *fs.MountSource) *fs.Inode { 241 fdid := &fdInfoDir{ 242 Dir: *ramfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0500)), 243 t: t, 244 } 245 return newProcInode(ctx, fdid, msrc, fs.SpecialDirectory, t) 246 } 247 248 // Lookup loads an fd in /proc/TID/fdinfo into a Dirent. 249 func (fdid *fdInfoDir) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) { 250 inode, err := walkDescriptors(fdid.t, p, func(file *fs.File, fdFlags kernel.FDFlags) *fs.Inode { 251 // TODO(b/121266871): Using a static inode here means that the 252 // data can be out-of-date if, for instance, the flags on the 253 // FD change before we read this file. We should switch to 254 // generating the data on Read(). Also, we should include pos, 255 // locks, and other data. For now we only have flags. 256 // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt 257 flags := file.Flags().ToLinux() | fdFlags.ToLinuxFileFlags() 258 file.DecRef(ctx) 259 contents := []byte(fmt.Sprintf("flags:\t0%o\n", flags)) 260 return newStaticProcInode(ctx, dir.MountSource, contents) 261 }) 262 if err != nil { 263 return nil, err 264 } 265 return fs.NewDirent(ctx, inode, p), nil 266 } 267 268 // GetFile implements fs.FileOperations.GetFile. 269 func (fdid *fdInfoDir) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 270 fops := &fdDirFile{ 271 isInfoFile: true, 272 t: fdid.t, 273 } 274 return fs.NewFile(ctx, dirent, flags, fops), nil 275 } 276 277 // LINT.ThenChange(../../fsimpl/proc/task_files.go)