github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/pipefs/pipefs.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 pipefs provides the filesystem implementation backing
    16  // Kernel.PipeMount.
    17  package pipefs
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    23  	"github.com/SagerNet/gvisor/pkg/context"
    24  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    25  	"github.com/SagerNet/gvisor/pkg/fspath"
    26  	"github.com/SagerNet/gvisor/pkg/hostarch"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/fsimpl/kernfs"
    28  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    29  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/pipe"
    30  	ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time"
    31  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    32  )
    33  
    34  // +stateify savable
    35  type filesystemType struct{}
    36  
    37  // Name implements vfs.FilesystemType.Name.
    38  func (filesystemType) Name() string {
    39  	return "pipefs"
    40  }
    41  
    42  // Release implements vfs.FilesystemType.Release.
    43  func (filesystemType) Release(ctx context.Context) {}
    44  
    45  // GetFilesystem implements vfs.FilesystemType.GetFilesystem.
    46  func (filesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
    47  	panic("pipefs.filesystemType.GetFilesystem should never be called")
    48  }
    49  
    50  // +stateify savable
    51  type filesystem struct {
    52  	kernfs.Filesystem
    53  
    54  	devMinor uint32
    55  }
    56  
    57  // NewFilesystem sets up and returns a new vfs.Filesystem implemented by pipefs.
    58  func NewFilesystem(vfsObj *vfs.VirtualFilesystem) (*vfs.Filesystem, error) {
    59  	devMinor, err := vfsObj.GetAnonBlockDevMinor()
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	fs := &filesystem{
    64  		devMinor: devMinor,
    65  	}
    66  	fs.Filesystem.VFSFilesystem().Init(vfsObj, filesystemType{}, fs)
    67  	return fs.Filesystem.VFSFilesystem(), nil
    68  }
    69  
    70  // Release implements vfs.FilesystemImpl.Release.
    71  func (fs *filesystem) Release(ctx context.Context) {
    72  	fs.Filesystem.VFSFilesystem().VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
    73  	fs.Filesystem.Release(ctx)
    74  }
    75  
    76  // PrependPath implements vfs.FilesystemImpl.PrependPath.
    77  func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
    78  	inode := vd.Dentry().Impl().(*kernfs.Dentry).Inode().(*inode)
    79  	b.PrependComponent(fmt.Sprintf("pipe:[%d]", inode.ino))
    80  	return vfs.PrependPathSyntheticError{}
    81  }
    82  
    83  // MountOptions implements vfs.FilesystemImpl.MountOptions.
    84  func (fs *filesystem) MountOptions() string {
    85  	return ""
    86  }
    87  
    88  // inode implements kernfs.Inode.
    89  //
    90  // +stateify savable
    91  type inode struct {
    92  	kernfs.InodeNotDirectory
    93  	kernfs.InodeNotSymlink
    94  	kernfs.InodeNoopRefCount
    95  
    96  	locks vfs.FileLocks
    97  	pipe  *pipe.VFSPipe
    98  
    99  	ino uint64
   100  	uid auth.KUID
   101  	gid auth.KGID
   102  	// We use the creation timestamp for all of atime, mtime, and ctime.
   103  	ctime ktime.Time
   104  }
   105  
   106  func newInode(ctx context.Context, fs *filesystem) *inode {
   107  	creds := auth.CredentialsFromContext(ctx)
   108  	return &inode{
   109  		pipe:  pipe.NewVFSPipe(false /* isNamed */, pipe.DefaultPipeSize),
   110  		ino:   fs.Filesystem.NextIno(),
   111  		uid:   creds.EffectiveKUID,
   112  		gid:   creds.EffectiveKGID,
   113  		ctime: ktime.NowFromContext(ctx),
   114  	}
   115  }
   116  
   117  const pipeMode = 0600 | linux.S_IFIFO
   118  
   119  // CheckPermissions implements kernfs.Inode.CheckPermissions.
   120  func (i *inode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
   121  	return vfs.GenericCheckPermissions(creds, ats, pipeMode, i.uid, i.gid)
   122  }
   123  
   124  // Mode implements kernfs.Inode.Mode.
   125  func (i *inode) Mode() linux.FileMode {
   126  	return pipeMode
   127  }
   128  
   129  // Stat implements kernfs.Inode.Stat.
   130  func (i *inode) Stat(_ context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
   131  	ts := linux.NsecToStatxTimestamp(i.ctime.Nanoseconds())
   132  	return linux.Statx{
   133  		Mask:     linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_NLINK | linux.STATX_UID | linux.STATX_GID | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME | linux.STATX_INO | linux.STATX_SIZE | linux.STATX_BLOCKS,
   134  		Blksize:  hostarch.PageSize,
   135  		Nlink:    1,
   136  		UID:      uint32(i.uid),
   137  		GID:      uint32(i.gid),
   138  		Mode:     pipeMode,
   139  		Ino:      i.ino,
   140  		Size:     0,
   141  		Blocks:   0,
   142  		Atime:    ts,
   143  		Ctime:    ts,
   144  		Mtime:    ts,
   145  		DevMajor: linux.UNNAMED_MAJOR,
   146  		DevMinor: vfsfs.Impl().(*filesystem).devMinor,
   147  	}, nil
   148  }
   149  
   150  // SetStat implements kernfs.Inode.SetStat.
   151  func (i *inode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
   152  	if opts.Stat.Mask == 0 {
   153  		return nil
   154  	}
   155  	return linuxerr.EPERM
   156  }
   157  
   158  // Open implements kernfs.Inode.Open.
   159  func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
   160  	return i.pipe.Open(ctx, rp.Mount(), d.VFSDentry(), opts.Flags, &i.locks)
   161  }
   162  
   163  // StatFS implements kernfs.Inode.StatFS.
   164  func (i *inode) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, error) {
   165  	return vfs.GenericStatFS(linux.PIPEFS_MAGIC), nil
   166  }
   167  
   168  // NewConnectedPipeFDs returns a pair of FileDescriptions representing the read
   169  // and write ends of a newly-created pipe, as for pipe(2) and pipe2(2).
   170  //
   171  // Preconditions: mnt.Filesystem() must have been returned by NewFilesystem().
   172  func NewConnectedPipeFDs(ctx context.Context, mnt *vfs.Mount, flags uint32) (*vfs.FileDescription, *vfs.FileDescription, error) {
   173  	fs := mnt.Filesystem().Impl().(*filesystem)
   174  	inode := newInode(ctx, fs)
   175  	var d kernfs.Dentry
   176  	d.Init(&fs.Filesystem, inode)
   177  	defer d.DecRef(ctx)
   178  	return inode.pipe.ReaderWriterPair(ctx, mnt, d.VFSDentry(), flags)
   179  }