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