github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/fsutil/inode.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 fsutil
    16  
    17  import (
    18  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    19  	"github.com/SagerNet/gvisor/pkg/context"
    20  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    21  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    22  	ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/memmap"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/socket/unix/transport"
    25  	"github.com/SagerNet/gvisor/pkg/sync"
    26  	"github.com/SagerNet/gvisor/pkg/syserror"
    27  	"github.com/SagerNet/gvisor/pkg/waiter"
    28  )
    29  
    30  // SimpleFileInode is a simple implementation of InodeOperations.
    31  //
    32  // +stateify savable
    33  type SimpleFileInode struct {
    34  	InodeGenericChecker       `state:"nosave"`
    35  	InodeNoExtendedAttributes `state:"nosave"`
    36  	InodeNoopRelease          `state:"nosave"`
    37  	InodeNoopWriteOut         `state:"nosave"`
    38  	InodeNotAllocatable       `state:"nosave"`
    39  	InodeNotDirectory         `state:"nosave"`
    40  	InodeNotMappable          `state:"nosave"`
    41  	InodeNotOpenable          `state:"nosave"`
    42  	InodeNotSocket            `state:"nosave"`
    43  	InodeNotSymlink           `state:"nosave"`
    44  	InodeNotTruncatable       `state:"nosave"`
    45  	InodeNotVirtual           `state:"nosave"`
    46  
    47  	InodeSimpleAttributes
    48  }
    49  
    50  // NewSimpleFileInode returns a new SimpleFileInode.
    51  func NewSimpleFileInode(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) *SimpleFileInode {
    52  	return &SimpleFileInode{
    53  		InodeSimpleAttributes: NewInodeSimpleAttributes(ctx, owner, perms, typ),
    54  	}
    55  }
    56  
    57  // NoReadWriteFileInode is an implementation of InodeOperations that supports
    58  // opening files that are not readable or writeable.
    59  //
    60  // +stateify savable
    61  type NoReadWriteFileInode struct {
    62  	InodeGenericChecker       `state:"nosave"`
    63  	InodeNoExtendedAttributes `state:"nosave"`
    64  	InodeNoopRelease          `state:"nosave"`
    65  	InodeNoopWriteOut         `state:"nosave"`
    66  	InodeNotAllocatable       `state:"nosave"`
    67  	InodeNotDirectory         `state:"nosave"`
    68  	InodeNotMappable          `state:"nosave"`
    69  	InodeNotSocket            `state:"nosave"`
    70  	InodeNotSymlink           `state:"nosave"`
    71  	InodeNotTruncatable       `state:"nosave"`
    72  	InodeNotVirtual           `state:"nosave"`
    73  
    74  	InodeSimpleAttributes
    75  }
    76  
    77  // NewNoReadWriteFileInode returns a new NoReadWriteFileInode.
    78  func NewNoReadWriteFileInode(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) *NoReadWriteFileInode {
    79  	return &NoReadWriteFileInode{
    80  		InodeSimpleAttributes: NewInodeSimpleAttributes(ctx, owner, perms, typ),
    81  	}
    82  }
    83  
    84  // GetFile implements fs.InodeOperations.GetFile.
    85  func (*NoReadWriteFileInode) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
    86  	return fs.NewFile(ctx, dirent, flags, &NoReadWriteFile{}), nil
    87  }
    88  
    89  // InodeSimpleAttributes implements methods for updating in-memory unstable
    90  // attributes.
    91  //
    92  // +stateify savable
    93  type InodeSimpleAttributes struct {
    94  	// fsType is the immutable filesystem type that will be returned by
    95  	// StatFS.
    96  	fsType uint64
    97  
    98  	// mu protects unstable.
    99  	mu       sync.RWMutex `state:"nosave"`
   100  	unstable fs.UnstableAttr
   101  }
   102  
   103  // NewInodeSimpleAttributes returns a new InodeSimpleAttributes with the given
   104  // owner and permissions, and all timestamps set to the current time.
   105  func NewInodeSimpleAttributes(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) InodeSimpleAttributes {
   106  	return NewInodeSimpleAttributesWithUnstable(fs.WithCurrentTime(ctx, fs.UnstableAttr{
   107  		Owner: owner,
   108  		Perms: perms,
   109  	}), typ)
   110  }
   111  
   112  // NewInodeSimpleAttributesWithUnstable returns a new InodeSimpleAttributes
   113  // with the given unstable attributes.
   114  func NewInodeSimpleAttributesWithUnstable(uattr fs.UnstableAttr, typ uint64) InodeSimpleAttributes {
   115  	return InodeSimpleAttributes{
   116  		fsType:   typ,
   117  		unstable: uattr,
   118  	}
   119  }
   120  
   121  // UnstableAttr implements fs.InodeOperations.UnstableAttr.
   122  func (i *InodeSimpleAttributes) UnstableAttr(ctx context.Context, _ *fs.Inode) (fs.UnstableAttr, error) {
   123  	i.mu.RLock()
   124  	u := i.unstable
   125  	i.mu.RUnlock()
   126  	return u, nil
   127  }
   128  
   129  // SetPermissions implements fs.InodeOperations.SetPermissions.
   130  func (i *InodeSimpleAttributes) SetPermissions(ctx context.Context, _ *fs.Inode, p fs.FilePermissions) bool {
   131  	i.mu.Lock()
   132  	i.unstable.SetPermissions(ctx, p)
   133  	i.mu.Unlock()
   134  	return true
   135  }
   136  
   137  // SetOwner implements fs.InodeOperations.SetOwner.
   138  func (i *InodeSimpleAttributes) SetOwner(ctx context.Context, _ *fs.Inode, owner fs.FileOwner) error {
   139  	i.mu.Lock()
   140  	i.unstable.SetOwner(ctx, owner)
   141  	i.mu.Unlock()
   142  	return nil
   143  }
   144  
   145  // SetTimestamps implements fs.InodeOperations.SetTimestamps.
   146  func (i *InodeSimpleAttributes) SetTimestamps(ctx context.Context, _ *fs.Inode, ts fs.TimeSpec) error {
   147  	i.mu.Lock()
   148  	i.unstable.SetTimestamps(ctx, ts)
   149  	i.mu.Unlock()
   150  	return nil
   151  }
   152  
   153  // AddLink implements fs.InodeOperations.AddLink.
   154  func (i *InodeSimpleAttributes) AddLink() {
   155  	i.mu.Lock()
   156  	i.unstable.Links++
   157  	i.mu.Unlock()
   158  }
   159  
   160  // DropLink implements fs.InodeOperations.DropLink.
   161  func (i *InodeSimpleAttributes) DropLink() {
   162  	i.mu.Lock()
   163  	i.unstable.Links--
   164  	i.mu.Unlock()
   165  }
   166  
   167  // StatFS implements fs.InodeOperations.StatFS.
   168  func (i *InodeSimpleAttributes) StatFS(context.Context) (fs.Info, error) {
   169  	if i.fsType == 0 {
   170  		return fs.Info{}, syserror.ENOSYS
   171  	}
   172  	return fs.Info{Type: i.fsType}, nil
   173  }
   174  
   175  // NotifyAccess updates the access time.
   176  func (i *InodeSimpleAttributes) NotifyAccess(ctx context.Context) {
   177  	i.mu.Lock()
   178  	i.unstable.AccessTime = ktime.NowFromContext(ctx)
   179  	i.mu.Unlock()
   180  }
   181  
   182  // NotifyModification updates the modification time.
   183  func (i *InodeSimpleAttributes) NotifyModification(ctx context.Context) {
   184  	i.mu.Lock()
   185  	i.unstable.ModificationTime = ktime.NowFromContext(ctx)
   186  	i.mu.Unlock()
   187  }
   188  
   189  // NotifyStatusChange updates the status change time.
   190  func (i *InodeSimpleAttributes) NotifyStatusChange(ctx context.Context) {
   191  	i.mu.Lock()
   192  	i.unstable.StatusChangeTime = ktime.NowFromContext(ctx)
   193  	i.mu.Unlock()
   194  }
   195  
   196  // NotifyModificationAndStatusChange updates the modification and status change
   197  // times.
   198  func (i *InodeSimpleAttributes) NotifyModificationAndStatusChange(ctx context.Context) {
   199  	i.mu.Lock()
   200  	now := ktime.NowFromContext(ctx)
   201  	i.unstable.ModificationTime = now
   202  	i.unstable.StatusChangeTime = now
   203  	i.mu.Unlock()
   204  }
   205  
   206  // InodeSimpleExtendedAttributes implements
   207  // fs.InodeOperations.{Get,Set,List}Xattr.
   208  //
   209  // +stateify savable
   210  type InodeSimpleExtendedAttributes struct {
   211  	// mu protects xattrs.
   212  	mu     sync.RWMutex `state:"nosave"`
   213  	xattrs map[string]string
   214  }
   215  
   216  // GetXattr implements fs.InodeOperations.GetXattr.
   217  func (i *InodeSimpleExtendedAttributes) GetXattr(_ context.Context, _ *fs.Inode, name string, _ uint64) (string, error) {
   218  	i.mu.RLock()
   219  	value, ok := i.xattrs[name]
   220  	i.mu.RUnlock()
   221  	if !ok {
   222  		return "", linuxerr.ENOATTR
   223  	}
   224  	return value, nil
   225  }
   226  
   227  // SetXattr implements fs.InodeOperations.SetXattr.
   228  func (i *InodeSimpleExtendedAttributes) SetXattr(_ context.Context, _ *fs.Inode, name, value string, flags uint32) error {
   229  	i.mu.Lock()
   230  	defer i.mu.Unlock()
   231  	if i.xattrs == nil {
   232  		if flags&linux.XATTR_REPLACE != 0 {
   233  			return linuxerr.ENODATA
   234  		}
   235  		i.xattrs = make(map[string]string)
   236  	}
   237  
   238  	_, ok := i.xattrs[name]
   239  	if ok && flags&linux.XATTR_CREATE != 0 {
   240  		return syserror.EEXIST
   241  	}
   242  	if !ok && flags&linux.XATTR_REPLACE != 0 {
   243  		return linuxerr.ENODATA
   244  	}
   245  
   246  	i.xattrs[name] = value
   247  	return nil
   248  }
   249  
   250  // ListXattr implements fs.InodeOperations.ListXattr.
   251  func (i *InodeSimpleExtendedAttributes) ListXattr(context.Context, *fs.Inode, uint64) (map[string]struct{}, error) {
   252  	i.mu.RLock()
   253  	names := make(map[string]struct{}, len(i.xattrs))
   254  	for name := range i.xattrs {
   255  		names[name] = struct{}{}
   256  	}
   257  	i.mu.RUnlock()
   258  	return names, nil
   259  }
   260  
   261  // RemoveXattr implements fs.InodeOperations.RemoveXattr.
   262  func (i *InodeSimpleExtendedAttributes) RemoveXattr(_ context.Context, _ *fs.Inode, name string) error {
   263  	i.mu.Lock()
   264  	defer i.mu.Unlock()
   265  	if _, ok := i.xattrs[name]; ok {
   266  		delete(i.xattrs, name)
   267  		return nil
   268  	}
   269  	return linuxerr.ENOATTR
   270  }
   271  
   272  // staticFile is a file with static contents. It is returned by
   273  // InodeStaticFileGetter.GetFile.
   274  //
   275  // +stateify savable
   276  type staticFile struct {
   277  	FileGenericSeek          `state:"nosave"`
   278  	FileNoIoctl              `state:"nosave"`
   279  	FileNoMMap               `state:"nosave"`
   280  	FileNoSplice             `state:"nosave"`
   281  	FileNoopFsync            `state:"nosave"`
   282  	FileNoopFlush            `state:"nosave"`
   283  	FileNoopRelease          `state:"nosave"`
   284  	FileNoopWrite            `state:"nosave"`
   285  	FileNotDirReaddir        `state:"nosave"`
   286  	FileUseInodeUnstableAttr `state:"nosave"`
   287  	waiter.AlwaysReady       `state:"nosave"`
   288  
   289  	FileStaticContentReader
   290  }
   291  
   292  // InodeNoStatFS implement StatFS by retuning ENOSYS.
   293  type InodeNoStatFS struct{}
   294  
   295  // StatFS implements fs.InodeOperations.StatFS.
   296  func (InodeNoStatFS) StatFS(context.Context) (fs.Info, error) {
   297  	return fs.Info{}, syserror.ENOSYS
   298  }
   299  
   300  // InodeStaticFileGetter implements GetFile for a file with static contents.
   301  //
   302  // +stateify savable
   303  type InodeStaticFileGetter struct {
   304  	Contents []byte
   305  }
   306  
   307  // GetFile implements fs.InodeOperations.GetFile.
   308  func (i *InodeStaticFileGetter) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
   309  	return fs.NewFile(ctx, dirent, flags, &staticFile{
   310  		FileStaticContentReader: NewFileStaticContentReader(i.Contents),
   311  	}), nil
   312  }
   313  
   314  // InodeNotMappable returns a nil memmap.Mappable.
   315  type InodeNotMappable struct{}
   316  
   317  // Mappable implements fs.InodeOperations.Mappable.
   318  func (InodeNotMappable) Mappable(*fs.Inode) memmap.Mappable {
   319  	return nil
   320  }
   321  
   322  // InodeNoopWriteOut is a no-op implementation of fs.InodeOperations.WriteOut.
   323  type InodeNoopWriteOut struct{}
   324  
   325  // WriteOut is a no-op.
   326  func (InodeNoopWriteOut) WriteOut(context.Context, *fs.Inode) error {
   327  	return nil
   328  }
   329  
   330  // InodeNotDirectory can be used by Inodes that are not directories.
   331  type InodeNotDirectory struct{}
   332  
   333  // Lookup implements fs.InodeOperations.Lookup.
   334  func (InodeNotDirectory) Lookup(context.Context, *fs.Inode, string) (*fs.Dirent, error) {
   335  	return nil, syserror.ENOTDIR
   336  }
   337  
   338  // Create implements fs.InodeOperations.Create.
   339  func (InodeNotDirectory) Create(context.Context, *fs.Inode, string, fs.FileFlags, fs.FilePermissions) (*fs.File, error) {
   340  	return nil, syserror.ENOTDIR
   341  }
   342  
   343  // CreateLink implements fs.InodeOperations.CreateLink.
   344  func (InodeNotDirectory) CreateLink(context.Context, *fs.Inode, string, string) error {
   345  	return syserror.ENOTDIR
   346  }
   347  
   348  // CreateHardLink implements fs.InodeOperations.CreateHardLink.
   349  func (InodeNotDirectory) CreateHardLink(context.Context, *fs.Inode, *fs.Inode, string) error {
   350  	return syserror.ENOTDIR
   351  }
   352  
   353  // CreateDirectory implements fs.InodeOperations.CreateDirectory.
   354  func (InodeNotDirectory) CreateDirectory(context.Context, *fs.Inode, string, fs.FilePermissions) error {
   355  	return syserror.ENOTDIR
   356  }
   357  
   358  // Bind implements fs.InodeOperations.Bind.
   359  func (InodeNotDirectory) Bind(context.Context, *fs.Inode, string, transport.BoundEndpoint, fs.FilePermissions) (*fs.Dirent, error) {
   360  	return nil, syserror.ENOTDIR
   361  }
   362  
   363  // CreateFifo implements fs.InodeOperations.CreateFifo.
   364  func (InodeNotDirectory) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error {
   365  	return syserror.ENOTDIR
   366  }
   367  
   368  // Remove implements fs.InodeOperations.Remove.
   369  func (InodeNotDirectory) Remove(context.Context, *fs.Inode, string) error {
   370  	return syserror.ENOTDIR
   371  }
   372  
   373  // RemoveDirectory implements fs.InodeOperations.RemoveDirectory.
   374  func (InodeNotDirectory) RemoveDirectory(context.Context, *fs.Inode, string) error {
   375  	return syserror.ENOTDIR
   376  }
   377  
   378  // Rename implements fs.FileOperations.Rename.
   379  func (InodeNotDirectory) Rename(context.Context, *fs.Inode, *fs.Inode, string, *fs.Inode, string, bool) error {
   380  	return linuxerr.EINVAL
   381  }
   382  
   383  // InodeNotSocket can be used by Inodes that are not sockets.
   384  type InodeNotSocket struct{}
   385  
   386  // BoundEndpoint implements fs.InodeOperations.BoundEndpoint.
   387  func (InodeNotSocket) BoundEndpoint(*fs.Inode, string) transport.BoundEndpoint {
   388  	return nil
   389  }
   390  
   391  // InodeNotTruncatable can be used by Inodes that cannot be truncated.
   392  type InodeNotTruncatable struct{}
   393  
   394  // Truncate implements fs.InodeOperations.Truncate.
   395  func (InodeNotTruncatable) Truncate(context.Context, *fs.Inode, int64) error {
   396  	return linuxerr.EINVAL
   397  }
   398  
   399  // InodeIsDirTruncate implements fs.InodeOperations.Truncate for directories.
   400  type InodeIsDirTruncate struct{}
   401  
   402  // Truncate implements fs.InodeOperations.Truncate.
   403  func (InodeIsDirTruncate) Truncate(context.Context, *fs.Inode, int64) error {
   404  	return syserror.EISDIR
   405  }
   406  
   407  // InodeNoopTruncate implements fs.InodeOperations.Truncate as a noop.
   408  type InodeNoopTruncate struct{}
   409  
   410  // Truncate implements fs.InodeOperations.Truncate.
   411  func (InodeNoopTruncate) Truncate(context.Context, *fs.Inode, int64) error {
   412  	return nil
   413  }
   414  
   415  // InodeNotRenameable can be used by Inodes that cannot be truncated.
   416  type InodeNotRenameable struct{}
   417  
   418  // Rename implements fs.InodeOperations.Rename.
   419  func (InodeNotRenameable) Rename(context.Context, *fs.Inode, *fs.Inode, string, *fs.Inode, string, bool) error {
   420  	return linuxerr.EINVAL
   421  }
   422  
   423  // InodeNotOpenable can be used by Inodes that cannot be opened.
   424  type InodeNotOpenable struct{}
   425  
   426  // GetFile implements fs.InodeOperations.GetFile.
   427  func (InodeNotOpenable) GetFile(context.Context, *fs.Dirent, fs.FileFlags) (*fs.File, error) {
   428  	return nil, syserror.EIO
   429  }
   430  
   431  // InodeNotVirtual can be used by Inodes that are not virtual.
   432  type InodeNotVirtual struct{}
   433  
   434  // IsVirtual implements fs.InodeOperations.IsVirtual.
   435  func (InodeNotVirtual) IsVirtual() bool {
   436  	return false
   437  }
   438  
   439  // InodeVirtual can be used by Inodes that are virtual.
   440  type InodeVirtual struct{}
   441  
   442  // IsVirtual implements fs.InodeOperations.IsVirtual.
   443  func (InodeVirtual) IsVirtual() bool {
   444  	return true
   445  }
   446  
   447  // InodeNotSymlink can be used by Inodes that are not symlinks.
   448  type InodeNotSymlink struct{}
   449  
   450  // Readlink implements fs.InodeOperations.Readlink.
   451  func (InodeNotSymlink) Readlink(context.Context, *fs.Inode) (string, error) {
   452  	return "", linuxerr.ENOLINK
   453  }
   454  
   455  // Getlink implements fs.InodeOperations.Getlink.
   456  func (InodeNotSymlink) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
   457  	return nil, linuxerr.ENOLINK
   458  }
   459  
   460  // InodeNoExtendedAttributes can be used by Inodes that do not support
   461  // extended attributes.
   462  type InodeNoExtendedAttributes struct{}
   463  
   464  // GetXattr implements fs.InodeOperations.GetXattr.
   465  func (InodeNoExtendedAttributes) GetXattr(context.Context, *fs.Inode, string, uint64) (string, error) {
   466  	return "", syserror.EOPNOTSUPP
   467  }
   468  
   469  // SetXattr implements fs.InodeOperations.SetXattr.
   470  func (InodeNoExtendedAttributes) SetXattr(context.Context, *fs.Inode, string, string, uint32) error {
   471  	return syserror.EOPNOTSUPP
   472  }
   473  
   474  // ListXattr implements fs.InodeOperations.ListXattr.
   475  func (InodeNoExtendedAttributes) ListXattr(context.Context, *fs.Inode, uint64) (map[string]struct{}, error) {
   476  	return nil, syserror.EOPNOTSUPP
   477  }
   478  
   479  // RemoveXattr implements fs.InodeOperations.RemoveXattr.
   480  func (InodeNoExtendedAttributes) RemoveXattr(context.Context, *fs.Inode, string) error {
   481  	return syserror.EOPNOTSUPP
   482  }
   483  
   484  // InodeNoopRelease implements fs.InodeOperations.Release as a noop.
   485  type InodeNoopRelease struct{}
   486  
   487  // Release implements fs.InodeOperations.Release.
   488  func (InodeNoopRelease) Release(context.Context) {}
   489  
   490  // InodeGenericChecker implements fs.InodeOperations.Check with a generic
   491  // implementation.
   492  type InodeGenericChecker struct{}
   493  
   494  // Check implements fs.InodeOperations.Check.
   495  func (InodeGenericChecker) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
   496  	return fs.ContextCanAccessFile(ctx, inode, p)
   497  }
   498  
   499  // InodeDenyWriteChecker implements fs.InodeOperations.Check which denies all
   500  // write operations.
   501  type InodeDenyWriteChecker struct{}
   502  
   503  // Check implements fs.InodeOperations.Check.
   504  func (InodeDenyWriteChecker) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
   505  	if p.Write {
   506  		return false
   507  	}
   508  	return fs.ContextCanAccessFile(ctx, inode, p)
   509  }
   510  
   511  //InodeNotAllocatable can be used by Inodes that do not support Allocate().
   512  type InodeNotAllocatable struct{}
   513  
   514  // Allocate implements fs.InodeOperations.Allocate.
   515  func (InodeNotAllocatable) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error {
   516  	return syserror.EOPNOTSUPP
   517  }
   518  
   519  // InodeNoopAllocate implements fs.InodeOperations.Allocate as a noop.
   520  type InodeNoopAllocate struct{}
   521  
   522  // Allocate implements fs.InodeOperations.Allocate.
   523  func (InodeNoopAllocate) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error {
   524  	return nil
   525  }
   526  
   527  // InodeIsDirAllocate implements fs.InodeOperations.Allocate for directories.
   528  type InodeIsDirAllocate struct{}
   529  
   530  // Allocate implements fs.InodeOperations.Allocate.
   531  func (InodeIsDirAllocate) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error {
   532  	return syserror.EISDIR
   533  }