github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/fsimpl/kernfs/fd_impl_util.go (about)

     1  // Copyright 2019 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 kernfs
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/ttpreport/gvisor-ligolo/pkg/abi/linux"
    21  	"github.com/ttpreport/gvisor-ligolo/pkg/context"
    22  	"github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr"
    23  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel/auth"
    24  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/memmap"
    25  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/vfs"
    26  	"github.com/ttpreport/gvisor-ligolo/pkg/sync"
    27  	"github.com/ttpreport/gvisor-ligolo/pkg/usermem"
    28  )
    29  
    30  // SeekEndConfig describes the SEEK_END behaviour for FDs.
    31  //
    32  // +stateify savable
    33  type SeekEndConfig int
    34  
    35  // Constants related to SEEK_END behaviour for FDs.
    36  const (
    37  	// Consider the end of the file to be after the final static entry. This is
    38  	// the default option.
    39  	SeekEndStaticEntries = iota
    40  	// Consider the end of the file to be at offset 0.
    41  	SeekEndZero
    42  )
    43  
    44  // GenericDirectoryFDOptions contains configuration for a GenericDirectoryFD.
    45  //
    46  // +stateify savable
    47  type GenericDirectoryFDOptions struct {
    48  	SeekEnd SeekEndConfig
    49  }
    50  
    51  // GenericDirectoryFD implements vfs.FileDescriptionImpl for a generic directory
    52  // inode that uses OrderChildren to track child nodes.
    53  //
    54  // Note that GenericDirectoryFD holds a lock over OrderedChildren while calling
    55  // IterDirents callback. The IterDirents callback therefore cannot hash or
    56  // unhash children, or recursively call IterDirents on the same underlying
    57  // inode.
    58  //
    59  // Must be initialize with Init before first use.
    60  //
    61  // Lock ordering: mu => children.mu.
    62  //
    63  // +stateify savable
    64  type GenericDirectoryFD struct {
    65  	vfs.FileDescriptionDefaultImpl
    66  	vfs.DirectoryFileDescriptionDefaultImpl
    67  	vfs.LockFD
    68  
    69  	// Immutable.
    70  	seekEnd SeekEndConfig
    71  
    72  	vfsfd    vfs.FileDescription
    73  	children *OrderedChildren
    74  
    75  	// mu protects the fields below.
    76  	mu sync.Mutex `state:"nosave"`
    77  
    78  	// off is the current directory offset. Protected by "mu".
    79  	off int64
    80  }
    81  
    82  // NewGenericDirectoryFD creates a new GenericDirectoryFD and returns its
    83  // dentry.
    84  func NewGenericDirectoryFD(m *vfs.Mount, d *Dentry, children *OrderedChildren, locks *vfs.FileLocks, opts *vfs.OpenOptions, fdOpts GenericDirectoryFDOptions) (*GenericDirectoryFD, error) {
    85  	fd := &GenericDirectoryFD{}
    86  	if err := fd.Init(children, locks, opts, fdOpts); err != nil {
    87  		return nil, err
    88  	}
    89  	if err := fd.vfsfd.Init(fd, opts.Flags, m, d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil {
    90  		return nil, err
    91  	}
    92  	return fd, nil
    93  }
    94  
    95  // Init initializes a GenericDirectoryFD. Use it when overriding
    96  // GenericDirectoryFD. Caller must call fd.VFSFileDescription.Init() with the
    97  // correct implementation.
    98  func (fd *GenericDirectoryFD) Init(children *OrderedChildren, locks *vfs.FileLocks, opts *vfs.OpenOptions, fdOpts GenericDirectoryFDOptions) error {
    99  	if vfs.AccessTypesForOpenFlags(opts)&vfs.MayWrite != 0 {
   100  		// Can't open directories for writing.
   101  		return linuxerr.EISDIR
   102  	}
   103  	fd.LockFD.Init(locks)
   104  	fd.seekEnd = fdOpts.SeekEnd
   105  	fd.children = children
   106  	return nil
   107  }
   108  
   109  // VFSFileDescription returns a pointer to the vfs.FileDescription representing
   110  // this object.
   111  func (fd *GenericDirectoryFD) VFSFileDescription() *vfs.FileDescription {
   112  	return &fd.vfsfd
   113  }
   114  
   115  // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
   116  func (fd *GenericDirectoryFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
   117  	return fd.FileDescriptionDefaultImpl.ConfigureMMap(ctx, opts)
   118  }
   119  
   120  // Read implmenets vfs.FileDescriptionImpl.Read.
   121  func (fd *GenericDirectoryFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
   122  	return fd.DirectoryFileDescriptionDefaultImpl.Read(ctx, dst, opts)
   123  }
   124  
   125  // PRead implmenets vfs.FileDescriptionImpl.PRead.
   126  func (fd *GenericDirectoryFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
   127  	return fd.DirectoryFileDescriptionDefaultImpl.PRead(ctx, dst, offset, opts)
   128  }
   129  
   130  // Write implements vfs.FileDescriptionImpl.Write.
   131  func (fd *GenericDirectoryFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
   132  	return fd.DirectoryFileDescriptionDefaultImpl.Write(ctx, src, opts)
   133  }
   134  
   135  // PWrite implements vfs.FileDescriptionImpl.PWrite.
   136  func (fd *GenericDirectoryFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
   137  	return fd.DirectoryFileDescriptionDefaultImpl.PWrite(ctx, src, offset, opts)
   138  }
   139  
   140  // Release implements vfs.FileDescriptionImpl.Release.
   141  func (fd *GenericDirectoryFD) Release(context.Context) {}
   142  
   143  func (fd *GenericDirectoryFD) filesystem() *vfs.Filesystem {
   144  	return fd.vfsfd.VirtualDentry().Mount().Filesystem()
   145  }
   146  
   147  func (fd *GenericDirectoryFD) dentry() *Dentry {
   148  	return fd.vfsfd.Dentry().Impl().(*Dentry)
   149  }
   150  
   151  func (fd *GenericDirectoryFD) inode() Inode {
   152  	return fd.dentry().inode
   153  }
   154  
   155  // IterDirents implements vfs.FileDescriptionImpl.IterDirents. IterDirents holds
   156  // o.mu when calling cb.
   157  func (fd *GenericDirectoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error {
   158  	fd.mu.Lock()
   159  	defer fd.mu.Unlock()
   160  
   161  	opts := vfs.StatOptions{Mask: linux.STATX_INO}
   162  	// Handle ".".
   163  	if fd.off == 0 {
   164  		stat, err := fd.inode().Stat(ctx, fd.filesystem(), opts)
   165  		if err != nil {
   166  			return err
   167  		}
   168  		dirent := vfs.Dirent{
   169  			Name:    ".",
   170  			Type:    linux.DT_DIR,
   171  			Ino:     stat.Ino,
   172  			NextOff: 1,
   173  		}
   174  		if err := cb.Handle(dirent); err != nil {
   175  			return err
   176  		}
   177  		fd.off++
   178  	}
   179  
   180  	// Handle "..".
   181  	if fd.off == 1 {
   182  		parentInode := genericParentOrSelf(fd.dentry()).inode
   183  		stat, err := parentInode.Stat(ctx, fd.filesystem(), opts)
   184  		if err != nil {
   185  			return err
   186  		}
   187  		dirent := vfs.Dirent{
   188  			Name:    "..",
   189  			Type:    linux.FileMode(stat.Mode).DirentType(),
   190  			Ino:     stat.Ino,
   191  			NextOff: 2,
   192  		}
   193  		if err := cb.Handle(dirent); err != nil {
   194  			return err
   195  		}
   196  		fd.off++
   197  	}
   198  
   199  	// Handle static children.
   200  	fd.children.mu.RLock()
   201  	defer fd.children.mu.RUnlock()
   202  	// fd.off accounts for "." and "..", but fd.children do not track
   203  	// these.
   204  	childIdx := fd.off - 2
   205  	for it := fd.children.nthLocked(childIdx); it != nil; it = it.Next() {
   206  		stat, err := it.inode.Stat(ctx, fd.filesystem(), opts)
   207  		if err != nil {
   208  			return err
   209  		}
   210  		dirent := vfs.Dirent{
   211  			Name:    it.name,
   212  			Type:    linux.FileMode(stat.Mode).DirentType(),
   213  			Ino:     stat.Ino,
   214  			NextOff: fd.off + 1,
   215  		}
   216  		if err := cb.Handle(dirent); err != nil {
   217  			return err
   218  		}
   219  		fd.off++
   220  	}
   221  
   222  	var err error
   223  	relOffset := fd.off - int64(len(fd.children.set)) - 2
   224  	fd.off, err = fd.inode().IterDirents(ctx, fd.vfsfd.Mount(), cb, fd.off, relOffset)
   225  	return err
   226  }
   227  
   228  // Seek implements vfs.FileDescriptionImpl.Seek.
   229  func (fd *GenericDirectoryFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
   230  	fd.mu.Lock()
   231  	defer fd.mu.Unlock()
   232  
   233  	switch whence {
   234  	case linux.SEEK_SET:
   235  		// Use offset as given.
   236  	case linux.SEEK_CUR:
   237  		offset += fd.off
   238  	case linux.SEEK_END:
   239  		switch fd.seekEnd {
   240  		case SeekEndStaticEntries:
   241  			fd.children.mu.RLock()
   242  			offset += int64(len(fd.children.set))
   243  			offset += 2 // '.' and '..' aren't tracked in children.
   244  			fd.children.mu.RUnlock()
   245  		case SeekEndZero:
   246  			// No-op: offset += 0.
   247  		default:
   248  			panic(fmt.Sprintf("Invalid GenericDirectoryFD.seekEnd = %v", fd.seekEnd))
   249  		}
   250  	default:
   251  		return 0, linuxerr.EINVAL
   252  	}
   253  	if offset < 0 {
   254  		return 0, linuxerr.EINVAL
   255  	}
   256  	fd.off = offset
   257  	return offset, nil
   258  }
   259  
   260  // Stat implements vfs.FileDescriptionImpl.Stat.
   261  func (fd *GenericDirectoryFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
   262  	fs := fd.filesystem()
   263  	inode := fd.inode()
   264  	return inode.Stat(ctx, fs, opts)
   265  }
   266  
   267  // SetStat implements vfs.FileDescriptionImpl.SetStat.
   268  func (fd *GenericDirectoryFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
   269  	creds := auth.CredentialsFromContext(ctx)
   270  	return fd.inode().SetStat(ctx, fd.filesystem(), creds, opts)
   271  }
   272  
   273  // Allocate implements vfs.FileDescriptionImpl.Allocate.
   274  func (fd *GenericDirectoryFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
   275  	return fd.DirectoryFileDescriptionDefaultImpl.Allocate(ctx, mode, offset, length)
   276  }