gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/vfs/anonfs.go (about)

     1  // Copyright 2020 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 vfs
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"gvisor.dev/gvisor/pkg/abi/linux"
    21  	"gvisor.dev/gvisor/pkg/context"
    22  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    23  	"gvisor.dev/gvisor/pkg/fspath"
    24  	"gvisor.dev/gvisor/pkg/hostarch"
    25  	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
    26  	"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
    27  )
    28  
    29  // NewAnonVirtualDentry returns a VirtualDentry with the given synthetic name,
    30  // consistent with Linux's fs/anon_inodes.c:anon_inode_getfile(). References
    31  // are taken on the returned VirtualDentry.
    32  func (vfs *VirtualFilesystem) NewAnonVirtualDentry(name string) VirtualDentry {
    33  	d := anonDentry{
    34  		name: name,
    35  	}
    36  	d.vfsd.Init(&d)
    37  	vfs.anonMount.IncRef()
    38  	// anonDentry no-ops refcounting.
    39  	return VirtualDentry{
    40  		mount:  vfs.anonMount,
    41  		dentry: &d.vfsd,
    42  	}
    43  }
    44  
    45  const (
    46  	anonfsBlockSize = hostarch.PageSize // via fs/libfs.c:pseudo_fs_fill_super()
    47  
    48  	// Mode, UID, and GID for a generic anonfs file.
    49  	anonFileMode = 0600 // no type is correct
    50  	anonFileUID  = auth.RootKUID
    51  	anonFileGID  = auth.RootKGID
    52  )
    53  
    54  // anonFilesystemType implements FilesystemType.
    55  //
    56  // +stateify savable
    57  type anonFilesystemType struct{}
    58  
    59  // GetFilesystem implements FilesystemType.GetFilesystem.
    60  func (anonFilesystemType) GetFilesystem(context.Context, *VirtualFilesystem, *auth.Credentials, string, GetFilesystemOptions) (*Filesystem, *Dentry, error) {
    61  	panic("cannot instaniate an anon filesystem")
    62  }
    63  
    64  // Name implements FilesystemType.Name.
    65  func (anonFilesystemType) Name() string {
    66  	return "none"
    67  }
    68  
    69  // Release implemenents FilesystemType.Release.
    70  func (anonFilesystemType) Release(ctx context.Context) {}
    71  
    72  // anonFilesystem is the implementation of FilesystemImpl that backs
    73  // VirtualDentries returned by VirtualFilesystem.NewAnonVirtualDentry().
    74  //
    75  // Since all Dentries in anonFilesystem are non-directories, all FilesystemImpl
    76  // methods that would require an anonDentry to be a directory return ENOTDIR.
    77  //
    78  // +stateify savable
    79  type anonFilesystem struct {
    80  	vfsfs Filesystem
    81  
    82  	devMinor uint32
    83  }
    84  
    85  // +stateify savable
    86  type anonDentry struct {
    87  	vfsd Dentry
    88  
    89  	name string
    90  
    91  	// Inotify watches for this dentry. Note that anonfs doesn't allow hardlinks
    92  	// and the dentry lifetime matches exactly with the file lifetime so it is
    93  	// okay to have the watches in the dentry itself.
    94  	watches Watches
    95  }
    96  
    97  // Release implements FilesystemImpl.Release.
    98  func (fs *anonFilesystem) Release(ctx context.Context) {
    99  }
   100  
   101  // Sync implements FilesystemImpl.Sync.
   102  func (fs *anonFilesystem) Sync(ctx context.Context) error {
   103  	return nil
   104  }
   105  
   106  // AccessAt implements vfs.Filesystem.Impl.AccessAt.
   107  func (fs *anonFilesystem) AccessAt(ctx context.Context, rp *ResolvingPath, creds *auth.Credentials, ats AccessTypes) error {
   108  	if !rp.Done() || rp.MustBeDir() {
   109  		return linuxerr.ENOTDIR
   110  	}
   111  	return GenericCheckPermissions(creds, ats, anonFileMode, anonFileUID, anonFileGID)
   112  }
   113  
   114  // GetDentryAt implements FilesystemImpl.GetDentryAt.
   115  func (fs *anonFilesystem) GetDentryAt(ctx context.Context, rp *ResolvingPath, opts GetDentryOptions) (*Dentry, error) {
   116  	if !rp.Done() || rp.MustBeDir() {
   117  		return nil, linuxerr.ENOTDIR
   118  	}
   119  	if opts.CheckSearchable {
   120  		return nil, linuxerr.ENOTDIR
   121  	}
   122  	// anonDentry no-ops refcounting.
   123  	return rp.Start(), nil
   124  }
   125  
   126  // GetParentDentryAt implements FilesystemImpl.GetParentDentryAt.
   127  func (fs *anonFilesystem) GetParentDentryAt(ctx context.Context, rp *ResolvingPath) (*Dentry, error) {
   128  	if !rp.Final() {
   129  		return nil, linuxerr.ENOTDIR
   130  	}
   131  	// anonDentry no-ops refcounting.
   132  	return rp.Start(), nil
   133  }
   134  
   135  // LinkAt implements FilesystemImpl.LinkAt.
   136  func (fs *anonFilesystem) LinkAt(ctx context.Context, rp *ResolvingPath, vd VirtualDentry) error {
   137  	if !rp.Final() {
   138  		return linuxerr.ENOTDIR
   139  	}
   140  	return linuxerr.EPERM
   141  }
   142  
   143  // MkdirAt implements FilesystemImpl.MkdirAt.
   144  func (fs *anonFilesystem) MkdirAt(ctx context.Context, rp *ResolvingPath, opts MkdirOptions) error {
   145  	if !rp.Final() {
   146  		return linuxerr.ENOTDIR
   147  	}
   148  	return linuxerr.EPERM
   149  }
   150  
   151  // MknodAt implements FilesystemImpl.MknodAt.
   152  func (fs *anonFilesystem) MknodAt(ctx context.Context, rp *ResolvingPath, opts MknodOptions) error {
   153  	if !rp.Final() {
   154  		return linuxerr.ENOTDIR
   155  	}
   156  	return linuxerr.EPERM
   157  }
   158  
   159  // OpenAt implements FilesystemImpl.OpenAt.
   160  func (fs *anonFilesystem) OpenAt(ctx context.Context, rp *ResolvingPath, opts OpenOptions) (*FileDescription, error) {
   161  	if !rp.Done() || rp.MustBeDir() {
   162  		return nil, linuxerr.ENOTDIR
   163  	}
   164  	return nil, linuxerr.ENODEV
   165  }
   166  
   167  // ReadlinkAt implements FilesystemImpl.ReadlinkAt.
   168  func (fs *anonFilesystem) ReadlinkAt(ctx context.Context, rp *ResolvingPath) (string, error) {
   169  	if !rp.Done() || rp.MustBeDir() {
   170  		return "", linuxerr.ENOTDIR
   171  	}
   172  	return "", linuxerr.EINVAL
   173  }
   174  
   175  // RenameAt implements FilesystemImpl.RenameAt.
   176  func (fs *anonFilesystem) RenameAt(ctx context.Context, rp *ResolvingPath, oldParentVD VirtualDentry, oldName string, opts RenameOptions) error {
   177  	if !rp.Final() {
   178  		return linuxerr.ENOTDIR
   179  	}
   180  	return linuxerr.EPERM
   181  }
   182  
   183  // RmdirAt implements FilesystemImpl.RmdirAt.
   184  func (fs *anonFilesystem) RmdirAt(ctx context.Context, rp *ResolvingPath) error {
   185  	if !rp.Final() {
   186  		return linuxerr.ENOTDIR
   187  	}
   188  	return linuxerr.EPERM
   189  }
   190  
   191  // SetStatAt implements FilesystemImpl.SetStatAt.
   192  func (fs *anonFilesystem) SetStatAt(ctx context.Context, rp *ResolvingPath, opts SetStatOptions) error {
   193  	if !rp.Done() || rp.MustBeDir() {
   194  		return linuxerr.ENOTDIR
   195  	}
   196  	// Linux actually permits anon_inode_inode's metadata to be set, which is
   197  	// visible to all users of anon_inode_inode. We just silently ignore
   198  	// metadata changes.
   199  	return nil
   200  }
   201  
   202  // StatAt implements FilesystemImpl.StatAt.
   203  func (fs *anonFilesystem) StatAt(ctx context.Context, rp *ResolvingPath, opts StatOptions) (linux.Statx, error) {
   204  	if !rp.Done() || rp.MustBeDir() {
   205  		return linux.Statx{}, linuxerr.ENOTDIR
   206  	}
   207  	// See fs/anon_inodes.c:anon_inode_init() => fs/libfs.c:alloc_anon_inode().
   208  	return linux.Statx{
   209  		Mask:     linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_NLINK | linux.STATX_UID | linux.STATX_GID | linux.STATX_INO | linux.STATX_SIZE | linux.STATX_BLOCKS,
   210  		Blksize:  anonfsBlockSize,
   211  		Nlink:    1,
   212  		UID:      uint32(anonFileUID),
   213  		GID:      uint32(anonFileGID),
   214  		Mode:     anonFileMode,
   215  		Ino:      1,
   216  		Size:     0,
   217  		Blocks:   0,
   218  		DevMajor: linux.UNNAMED_MAJOR,
   219  		DevMinor: fs.devMinor,
   220  	}, nil
   221  }
   222  
   223  // StatFSAt implements FilesystemImpl.StatFSAt.
   224  func (fs *anonFilesystem) StatFSAt(ctx context.Context, rp *ResolvingPath) (linux.Statfs, error) {
   225  	if !rp.Done() || rp.MustBeDir() {
   226  		return linux.Statfs{}, linuxerr.ENOTDIR
   227  	}
   228  	return linux.Statfs{
   229  		Type:      linux.ANON_INODE_FS_MAGIC,
   230  		BlockSize: anonfsBlockSize,
   231  	}, nil
   232  }
   233  
   234  // SymlinkAt implements FilesystemImpl.SymlinkAt.
   235  func (fs *anonFilesystem) SymlinkAt(ctx context.Context, rp *ResolvingPath, target string) error {
   236  	if !rp.Final() {
   237  		return linuxerr.ENOTDIR
   238  	}
   239  	return linuxerr.EPERM
   240  }
   241  
   242  // UnlinkAt implements FilesystemImpl.UnlinkAt.
   243  func (fs *anonFilesystem) UnlinkAt(ctx context.Context, rp *ResolvingPath) error {
   244  	if !rp.Final() {
   245  		return linuxerr.ENOTDIR
   246  	}
   247  	return linuxerr.EPERM
   248  }
   249  
   250  // BoundEndpointAt implements FilesystemImpl.BoundEndpointAt.
   251  func (fs *anonFilesystem) BoundEndpointAt(ctx context.Context, rp *ResolvingPath, opts BoundEndpointOptions) (transport.BoundEndpoint, error) {
   252  	if !rp.Final() {
   253  		return nil, linuxerr.ENOTDIR
   254  	}
   255  	if err := GenericCheckPermissions(rp.Credentials(), MayWrite, anonFileMode, anonFileUID, anonFileGID); err != nil {
   256  		return nil, err
   257  	}
   258  	return nil, linuxerr.ECONNREFUSED
   259  }
   260  
   261  // ListXattrAt implements FilesystemImpl.ListXattrAt.
   262  func (fs *anonFilesystem) ListXattrAt(ctx context.Context, rp *ResolvingPath, size uint64) ([]string, error) {
   263  	if !rp.Done() || rp.MustBeDir() {
   264  		return nil, linuxerr.ENOTDIR
   265  	}
   266  	return nil, nil
   267  }
   268  
   269  // GetXattrAt implements FilesystemImpl.GetXattrAt.
   270  func (fs *anonFilesystem) GetXattrAt(ctx context.Context, rp *ResolvingPath, opts GetXattrOptions) (string, error) {
   271  	if !rp.Done() || rp.MustBeDir() {
   272  		return "", linuxerr.ENOTDIR
   273  	}
   274  	return "", linuxerr.ENOTSUP
   275  }
   276  
   277  // SetXattrAt implements FilesystemImpl.SetXattrAt.
   278  func (fs *anonFilesystem) SetXattrAt(ctx context.Context, rp *ResolvingPath, opts SetXattrOptions) error {
   279  	if !rp.Done() || rp.MustBeDir() {
   280  		return linuxerr.ENOTDIR
   281  	}
   282  	return linuxerr.EPERM
   283  }
   284  
   285  // RemoveXattrAt implements FilesystemImpl.RemoveXattrAt.
   286  func (fs *anonFilesystem) RemoveXattrAt(ctx context.Context, rp *ResolvingPath, name string) error {
   287  	if !rp.Done() || rp.MustBeDir() {
   288  		return linuxerr.ENOTDIR
   289  	}
   290  	return linuxerr.EPERM
   291  }
   292  
   293  // PrependPath implements FilesystemImpl.PrependPath.
   294  func (fs *anonFilesystem) PrependPath(ctx context.Context, vfsroot, vd VirtualDentry, b *fspath.Builder) error {
   295  	b.PrependComponent(fmt.Sprintf("anon_inode:%s", vd.dentry.impl.(*anonDentry).name))
   296  	return PrependPathSyntheticError{}
   297  }
   298  
   299  // MountOptions implements FilesystemImpl.MountOptions.
   300  func (fs *anonFilesystem) MountOptions() string {
   301  	return ""
   302  }
   303  
   304  // IsDescendant implements FilesystemImpl.IsDescendant.
   305  func (fs *anonFilesystem) IsDescendant(vfsroot, vd VirtualDentry) bool { return vfsroot == vd }
   306  
   307  // IncRef implements DentryImpl.IncRef.
   308  func (d *anonDentry) IncRef() {
   309  	// no-op
   310  }
   311  
   312  // TryIncRef implements DentryImpl.TryIncRef.
   313  func (d *anonDentry) TryIncRef() bool {
   314  	return true
   315  }
   316  
   317  // DecRef implements DentryImpl.DecRef.
   318  func (d *anonDentry) DecRef(ctx context.Context) {
   319  	// no-op
   320  }
   321  
   322  // InotifyWithParent implements DentryImpl.InotifyWithParent.
   323  func (d *anonDentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et EventType) {
   324  	// d.parent doesn't exist.
   325  	d.watches.Notify(ctx, "", events, cookie, et, false /* unlinked */)
   326  }
   327  
   328  // Watches implements DentryImpl.Watches.
   329  func (d *anonDentry) Watches() *Watches {
   330  	return &d.watches
   331  }
   332  
   333  // OnZeroWatches implements Dentry.OnZeroWatches.
   334  func (d *anonDentry) OnZeroWatches(context.Context) {}