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