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  }