github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/tmpfs/tmpfs.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 tmpfs is a filesystem implementation backed by memory. 16 package tmpfs 17 18 import ( 19 "math" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 24 "github.com/SagerNet/gvisor/pkg/hostarch" 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/ramfs" 28 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 29 "github.com/SagerNet/gvisor/pkg/sentry/kernel/pipe" 30 "github.com/SagerNet/gvisor/pkg/sentry/socket/unix/transport" 31 "github.com/SagerNet/gvisor/pkg/sentry/usage" 32 ) 33 34 var fsInfo = fs.Info{ 35 Type: linux.TMPFS_MAGIC, 36 37 // tmpfs currently does not support configurable size limits. In Linux, 38 // such a tmpfs mount will return f_blocks == f_bfree == f_bavail == 0 from 39 // statfs(2). However, many applications treat this as having a size limit 40 // of 0. To work around this, claim to have a very large but non-zero size, 41 // chosen to ensure that BlockSize * Blocks does not overflow int64 (which 42 // applications may also handle incorrectly). 43 // TODO(b/29637826): allow configuring a tmpfs size and enforce it. 44 TotalBlocks: math.MaxInt64 / hostarch.PageSize, 45 FreeBlocks: math.MaxInt64 / hostarch.PageSize, 46 } 47 48 // rename implements fs.InodeOperations.Rename for tmpfs nodes. 49 func rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error { 50 // Don't allow renames across different mounts. 51 if newParent.MountSource != oldParent.MountSource { 52 return linuxerr.EXDEV 53 } 54 55 op := oldParent.InodeOperations.(*Dir) 56 np := newParent.InodeOperations.(*Dir) 57 return ramfs.Rename(ctx, op.ramfsDir, oldName, np.ramfsDir, newName, replacement) 58 } 59 60 // Dir is a directory. 61 // 62 // +stateify savable 63 type Dir struct { 64 fsutil.InodeGenericChecker `state:"nosave"` 65 fsutil.InodeIsDirTruncate `state:"nosave"` 66 fsutil.InodeNoopWriteOut `state:"nosave"` 67 fsutil.InodeNotMappable `state:"nosave"` 68 fsutil.InodeNotSocket `state:"nosave"` 69 fsutil.InodeNotSymlink `state:"nosave"` 70 fsutil.InodeVirtual `state:"nosave"` 71 72 // Ideally this would be embedded, so that we "inherit" all of the 73 // InodeOperations implemented by ramfs.Dir for free. 74 // 75 // However, ramfs.dirFileOperations stores a pointer to a ramfs.Dir, 76 // and our save/restore package does not allow saving a pointer to an 77 // embedded field elsewhere. 78 // 79 // Thus, we must make the ramfs.Dir is a field, and we delegate all the 80 // InodeOperation methods to it. 81 ramfsDir *ramfs.Dir 82 83 // kernel is used to allocate memory as storage for tmpfs Files. 84 kernel *kernel.Kernel 85 } 86 87 var _ fs.InodeOperations = (*Dir)(nil) 88 89 // NewDir returns a new directory. 90 func NewDir(ctx context.Context, contents map[string]*fs.Inode, owner fs.FileOwner, perms fs.FilePermissions, msrc *fs.MountSource, parent *fs.Inode) (*fs.Inode, error) { 91 // If the parent has setgid enabled, the new directory enables it and changes 92 // its GID. 93 if parent != nil { 94 parentUattr, err := parent.UnstableAttr(ctx) 95 if err != nil { 96 return nil, err 97 } 98 if parentUattr.Perms.SetGID { 99 owner.GID = parentUattr.Owner.GID 100 perms.SetGID = true 101 } 102 } 103 104 d := &Dir{ 105 ramfsDir: ramfs.NewDir(ctx, contents, owner, perms), 106 kernel: kernel.KernelFromContext(ctx), 107 } 108 109 // Manually set the CreateOps. 110 d.ramfsDir.CreateOps = d.newCreateOps() 111 112 return fs.NewInode(ctx, d, msrc, fs.StableAttr{ 113 DeviceID: tmpfsDevice.DeviceID(), 114 InodeID: tmpfsDevice.NextIno(), 115 BlockSize: hostarch.PageSize, 116 Type: fs.Directory, 117 }), nil 118 } 119 120 // afterLoad is invoked by stateify. 121 func (d *Dir) afterLoad() { 122 // Per NewDir, manually set the CreateOps. 123 d.ramfsDir.CreateOps = d.newCreateOps() 124 } 125 126 // GetFile implements fs.InodeOperations.GetFile. 127 func (d *Dir) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 128 return d.ramfsDir.GetFile(ctx, dirent, flags) 129 } 130 131 // AddLink implements fs.InodeOperations.AddLink. 132 func (d *Dir) AddLink() { 133 d.ramfsDir.AddLink() 134 } 135 136 // DropLink implements fs.InodeOperations.DropLink. 137 func (d *Dir) DropLink() { 138 d.ramfsDir.DropLink() 139 } 140 141 // Bind implements fs.InodeOperations.Bind. 142 func (d *Dir) Bind(ctx context.Context, dir *fs.Inode, name string, ep transport.BoundEndpoint, perms fs.FilePermissions) (*fs.Dirent, error) { 143 return d.ramfsDir.Bind(ctx, dir, name, ep, perms) 144 } 145 146 // Create implements fs.InodeOperations.Create. 147 func (d *Dir) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perms fs.FilePermissions) (*fs.File, error) { 148 return d.ramfsDir.Create(ctx, dir, name, flags, perms) 149 } 150 151 // CreateLink implements fs.InodeOperations.CreateLink. 152 func (d *Dir) CreateLink(ctx context.Context, dir *fs.Inode, oldname, newname string) error { 153 return d.ramfsDir.CreateLink(ctx, dir, oldname, newname) 154 } 155 156 // CreateHardLink implements fs.InodeOperations.CreateHardLink. 157 func (d *Dir) CreateHardLink(ctx context.Context, dir *fs.Inode, target *fs.Inode, name string) error { 158 return d.ramfsDir.CreateHardLink(ctx, dir, target, name) 159 } 160 161 // CreateDirectory implements fs.InodeOperations.CreateDirectory. 162 func (d *Dir) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, perms fs.FilePermissions) error { 163 return d.ramfsDir.CreateDirectory(ctx, dir, name, perms) 164 } 165 166 // CreateFifo implements fs.InodeOperations.CreateFifo. 167 func (d *Dir) CreateFifo(ctx context.Context, dir *fs.Inode, name string, perms fs.FilePermissions) error { 168 return d.ramfsDir.CreateFifo(ctx, dir, name, perms) 169 } 170 171 // GetXattr implements fs.InodeOperations.GetXattr. 172 func (d *Dir) GetXattr(ctx context.Context, i *fs.Inode, name string, size uint64) (string, error) { 173 return d.ramfsDir.GetXattr(ctx, i, name, size) 174 } 175 176 // SetXattr implements fs.InodeOperations.SetXattr. 177 func (d *Dir) SetXattr(ctx context.Context, i *fs.Inode, name, value string, flags uint32) error { 178 return d.ramfsDir.SetXattr(ctx, i, name, value, flags) 179 } 180 181 // ListXattr implements fs.InodeOperations.ListXattr. 182 func (d *Dir) ListXattr(ctx context.Context, i *fs.Inode, size uint64) (map[string]struct{}, error) { 183 return d.ramfsDir.ListXattr(ctx, i, size) 184 } 185 186 // RemoveXattr implements fs.InodeOperations.RemoveXattr. 187 func (d *Dir) RemoveXattr(ctx context.Context, i *fs.Inode, name string) error { 188 return d.ramfsDir.RemoveXattr(ctx, i, name) 189 } 190 191 // Lookup implements fs.InodeOperations.Lookup. 192 func (d *Dir) Lookup(ctx context.Context, i *fs.Inode, p string) (*fs.Dirent, error) { 193 return d.ramfsDir.Lookup(ctx, i, p) 194 } 195 196 // NotifyStatusChange implements fs.InodeOperations.NotifyStatusChange. 197 func (d *Dir) NotifyStatusChange(ctx context.Context) { 198 d.ramfsDir.NotifyStatusChange(ctx) 199 } 200 201 // Remove implements fs.InodeOperations.Remove. 202 func (d *Dir) Remove(ctx context.Context, i *fs.Inode, name string) error { 203 return d.ramfsDir.Remove(ctx, i, name) 204 } 205 206 // RemoveDirectory implements fs.InodeOperations.RemoveDirectory. 207 func (d *Dir) RemoveDirectory(ctx context.Context, i *fs.Inode, name string) error { 208 return d.ramfsDir.RemoveDirectory(ctx, i, name) 209 } 210 211 // UnstableAttr implements fs.InodeOperations.UnstableAttr. 212 func (d *Dir) UnstableAttr(ctx context.Context, i *fs.Inode) (fs.UnstableAttr, error) { 213 return d.ramfsDir.UnstableAttr(ctx, i) 214 } 215 216 // SetPermissions implements fs.InodeOperations.SetPermissions. 217 func (d *Dir) SetPermissions(ctx context.Context, i *fs.Inode, p fs.FilePermissions) bool { 218 return d.ramfsDir.SetPermissions(ctx, i, p) 219 } 220 221 // SetOwner implements fs.InodeOperations.SetOwner. 222 func (d *Dir) SetOwner(ctx context.Context, i *fs.Inode, owner fs.FileOwner) error { 223 return d.ramfsDir.SetOwner(ctx, i, owner) 224 } 225 226 // SetTimestamps implements fs.InodeOperations.SetTimestamps. 227 func (d *Dir) SetTimestamps(ctx context.Context, i *fs.Inode, ts fs.TimeSpec) error { 228 return d.ramfsDir.SetTimestamps(ctx, i, ts) 229 } 230 231 // newCreateOps builds the custom CreateOps for this Dir. 232 func (d *Dir) newCreateOps() *ramfs.CreateOps { 233 return &ramfs.CreateOps{ 234 NewDir: func(ctx context.Context, dir *fs.Inode, perms fs.FilePermissions) (*fs.Inode, error) { 235 return NewDir(ctx, nil, fs.FileOwnerFromContext(ctx), perms, dir.MountSource, dir) 236 }, 237 NewFile: func(ctx context.Context, dir *fs.Inode, perms fs.FilePermissions) (*fs.Inode, error) { 238 // If the parent has setgid enabled, change the GID of the new file. 239 owner := fs.FileOwnerFromContext(ctx) 240 parentUattr, err := dir.UnstableAttr(ctx) 241 if err != nil { 242 return nil, err 243 } 244 if parentUattr.Perms.SetGID { 245 owner.GID = parentUattr.Owner.GID 246 } 247 248 uattr := fs.WithCurrentTime(ctx, fs.UnstableAttr{ 249 Owner: owner, 250 Perms: perms, 251 // Always start unlinked. 252 Links: 0, 253 }) 254 iops := NewInMemoryFile(ctx, usage.Tmpfs, uattr) 255 return fs.NewInode(ctx, iops, dir.MountSource, fs.StableAttr{ 256 DeviceID: tmpfsDevice.DeviceID(), 257 InodeID: tmpfsDevice.NextIno(), 258 BlockSize: hostarch.PageSize, 259 Type: fs.RegularFile, 260 }), nil 261 }, 262 NewSymlink: func(ctx context.Context, dir *fs.Inode, target string) (*fs.Inode, error) { 263 return NewSymlink(ctx, target, fs.FileOwnerFromContext(ctx), dir.MountSource), nil 264 }, 265 NewBoundEndpoint: func(ctx context.Context, dir *fs.Inode, socket transport.BoundEndpoint, perms fs.FilePermissions) (*fs.Inode, error) { 266 return NewSocket(ctx, socket, fs.FileOwnerFromContext(ctx), perms, dir.MountSource), nil 267 }, 268 NewFifo: func(ctx context.Context, dir *fs.Inode, perms fs.FilePermissions) (*fs.Inode, error) { 269 return NewFifo(ctx, fs.FileOwnerFromContext(ctx), perms, dir.MountSource), nil 270 }, 271 } 272 } 273 274 // Rename implements fs.InodeOperations.Rename. 275 func (d *Dir) Rename(ctx context.Context, inode *fs.Inode, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error { 276 return rename(ctx, oldParent, oldName, newParent, newName, replacement) 277 } 278 279 // StatFS implements fs.InodeOperations.StatFS. 280 func (*Dir) StatFS(context.Context) (fs.Info, error) { 281 return fsInfo, nil 282 } 283 284 // Allocate implements fs.InodeOperations.Allocate. 285 func (d *Dir) Allocate(ctx context.Context, node *fs.Inode, offset, length int64) error { 286 return d.ramfsDir.Allocate(ctx, node, offset, length) 287 } 288 289 // Release implements fs.InodeOperations.Release. 290 func (d *Dir) Release(ctx context.Context) { 291 d.ramfsDir.Release(ctx) 292 } 293 294 // Symlink is a symlink. 295 // 296 // +stateify savable 297 type Symlink struct { 298 ramfs.Symlink 299 } 300 301 // NewSymlink returns a new symlink with the provided permissions. 302 func NewSymlink(ctx context.Context, target string, owner fs.FileOwner, msrc *fs.MountSource) *fs.Inode { 303 s := &Symlink{Symlink: *ramfs.NewSymlink(ctx, owner, target)} 304 return fs.NewInode(ctx, s, msrc, fs.StableAttr{ 305 DeviceID: tmpfsDevice.DeviceID(), 306 InodeID: tmpfsDevice.NextIno(), 307 BlockSize: hostarch.PageSize, 308 Type: fs.Symlink, 309 }) 310 } 311 312 // Rename implements fs.InodeOperations.Rename. 313 func (s *Symlink) Rename(ctx context.Context, inode *fs.Inode, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error { 314 return rename(ctx, oldParent, oldName, newParent, newName, replacement) 315 } 316 317 // StatFS returns the tmpfs info. 318 func (s *Symlink) StatFS(context.Context) (fs.Info, error) { 319 return fsInfo, nil 320 } 321 322 // Socket is a socket. 323 // 324 // +stateify savable 325 type Socket struct { 326 ramfs.Socket 327 fsutil.InodeNotTruncatable `state:"nosave"` 328 fsutil.InodeNotAllocatable `state:"nosave"` 329 } 330 331 // NewSocket returns a new socket with the provided permissions. 332 func NewSocket(ctx context.Context, socket transport.BoundEndpoint, owner fs.FileOwner, perms fs.FilePermissions, msrc *fs.MountSource) *fs.Inode { 333 s := &Socket{Socket: *ramfs.NewSocket(ctx, socket, owner, perms)} 334 return fs.NewInode(ctx, s, msrc, fs.StableAttr{ 335 DeviceID: tmpfsDevice.DeviceID(), 336 InodeID: tmpfsDevice.NextIno(), 337 BlockSize: hostarch.PageSize, 338 Type: fs.Socket, 339 }) 340 } 341 342 // Rename implements fs.InodeOperations.Rename. 343 func (s *Socket) Rename(ctx context.Context, inode *fs.Inode, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error { 344 return rename(ctx, oldParent, oldName, newParent, newName, replacement) 345 } 346 347 // StatFS returns the tmpfs info. 348 func (s *Socket) StatFS(context.Context) (fs.Info, error) { 349 return fsInfo, nil 350 } 351 352 // Fifo is a tmpfs named pipe. 353 // 354 // +stateify savable 355 type Fifo struct { 356 fs.InodeOperations 357 } 358 359 // NewFifo creates a new named pipe. 360 func NewFifo(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, msrc *fs.MountSource) *fs.Inode { 361 // First create a pipe. 362 p := pipe.NewPipe(true /* isNamed */, pipe.DefaultPipeSize) 363 364 // Build pipe InodeOperations. 365 iops := pipe.NewInodeOperations(ctx, perms, p) 366 367 // Wrap the iops with our Fifo. 368 fifoIops := &Fifo{iops} 369 370 // Build a new Inode. 371 return fs.NewInode(ctx, fifoIops, msrc, fs.StableAttr{ 372 DeviceID: tmpfsDevice.DeviceID(), 373 InodeID: tmpfsDevice.NextIno(), 374 BlockSize: hostarch.PageSize, 375 Type: fs.Pipe, 376 }) 377 } 378 379 // Rename implements fs.InodeOperations.Rename. 380 func (f *Fifo) Rename(ctx context.Context, inode *fs.Inode, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error { 381 return rename(ctx, oldParent, oldName, newParent, newName, replacement) 382 } 383 384 // StatFS returns the tmpfs info. 385 func (*Fifo) StatFS(context.Context) (fs.Info, error) { 386 return fsInfo, nil 387 }