github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/vfs/file_description_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 vfs
    16  
    17  import (
    18  	"bytes"
    19  	"io"
    20  	"math"
    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/sentry/arch"
    26  	fslock "github.com/ttpreport/gvisor-ligolo/pkg/sentry/fsimpl/lock"
    27  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/memmap"
    28  	"github.com/ttpreport/gvisor-ligolo/pkg/sync"
    29  	"github.com/ttpreport/gvisor-ligolo/pkg/usermem"
    30  	"github.com/ttpreport/gvisor-ligolo/pkg/waiter"
    31  )
    32  
    33  // The following design pattern is strongly recommended for filesystem
    34  // implementations to adapt:
    35  //   - Have a local fileDescription struct (containing FileDescription) which
    36  //     embeds FileDescriptionDefaultImpl and overrides the default methods
    37  //     which are common to all fd implementations for that filesystem like
    38  //     StatusFlags, SetStatusFlags, Stat, SetStat, StatFS, etc.
    39  //   - This should be embedded in all file description implementations as the
    40  //     first field by value.
    41  //   - Directory FDs would also embed DirectoryFileDescriptionDefaultImpl.
    42  
    43  // FileDescriptionDefaultImpl may be embedded by implementations of
    44  // FileDescriptionImpl to obtain implementations of many FileDescriptionImpl
    45  // methods with default behavior analogous to Linux's.
    46  //
    47  // +stateify savable
    48  type FileDescriptionDefaultImpl struct{}
    49  
    50  // OnClose implements FileDescriptionImpl.OnClose analogously to
    51  // file_operations::flush == NULL in Linux.
    52  func (FileDescriptionDefaultImpl) OnClose(ctx context.Context) error {
    53  	return nil
    54  }
    55  
    56  // StatFS implements FileDescriptionImpl.StatFS analogously to
    57  // super_operations::statfs == NULL in Linux.
    58  func (FileDescriptionDefaultImpl) StatFS(ctx context.Context) (linux.Statfs, error) {
    59  	return linux.Statfs{}, linuxerr.ENOSYS
    60  }
    61  
    62  // Allocate implements FileDescriptionImpl.Allocate analogously to
    63  // fallocate called on an invalid type of file in Linux.
    64  //
    65  // Note that directories can rely on this implementation even though they
    66  // should technically return EISDIR. Allocate should never be called for a
    67  // directory, because it requires a writable fd.
    68  func (FileDescriptionDefaultImpl) Allocate(ctx context.Context, mode, offset, length uint64) error {
    69  	return linuxerr.ENODEV
    70  }
    71  
    72  // Readiness implements waiter.Waitable.Readiness analogously to
    73  // file_operations::poll == NULL in Linux.
    74  func (FileDescriptionDefaultImpl) Readiness(mask waiter.EventMask) waiter.EventMask {
    75  	// include/linux/poll.h:vfs_poll() => DEFAULT_POLLMASK
    76  	return waiter.ReadableEvents | waiter.WritableEvents
    77  }
    78  
    79  // EventRegister implements waiter.Waitable.EventRegister analogously to
    80  // file_operations::poll == NULL in Linux.
    81  func (FileDescriptionDefaultImpl) EventRegister(e *waiter.Entry) error {
    82  	return nil
    83  }
    84  
    85  // EventUnregister implements waiter.Waitable.EventUnregister analogously to
    86  // file_operations::poll == NULL in Linux.
    87  func (FileDescriptionDefaultImpl) EventUnregister(e *waiter.Entry) {
    88  }
    89  
    90  // Epollable implements FileDescriptionImpl.Epollable.
    91  func (FileDescriptionDefaultImpl) Epollable() bool {
    92  	return false
    93  }
    94  
    95  // PRead implements FileDescriptionImpl.PRead analogously to
    96  // file_operations::read == file_operations::read_iter == NULL in Linux.
    97  func (FileDescriptionDefaultImpl) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
    98  	return 0, linuxerr.EINVAL
    99  }
   100  
   101  // Read implements FileDescriptionImpl.Read analogously to
   102  // file_operations::read == file_operations::read_iter == NULL in Linux.
   103  func (FileDescriptionDefaultImpl) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
   104  	return 0, linuxerr.EINVAL
   105  }
   106  
   107  // PWrite implements FileDescriptionImpl.PWrite analogously to
   108  // file_operations::write == file_operations::write_iter == NULL in Linux.
   109  func (FileDescriptionDefaultImpl) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
   110  	return 0, linuxerr.EINVAL
   111  }
   112  
   113  // Write implements FileDescriptionImpl.Write analogously to
   114  // file_operations::write == file_operations::write_iter == NULL in Linux.
   115  func (FileDescriptionDefaultImpl) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
   116  	return 0, linuxerr.EINVAL
   117  }
   118  
   119  // IterDirents implements FileDescriptionImpl.IterDirents analogously to
   120  // file_operations::iterate == file_operations::iterate_shared == NULL in
   121  // Linux.
   122  func (FileDescriptionDefaultImpl) IterDirents(ctx context.Context, cb IterDirentsCallback) error {
   123  	return linuxerr.ENOTDIR
   124  }
   125  
   126  // Seek implements FileDescriptionImpl.Seek analogously to
   127  // file_operations::llseek == NULL in Linux.
   128  func (FileDescriptionDefaultImpl) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
   129  	return 0, linuxerr.ESPIPE
   130  }
   131  
   132  // Sync implements FileDescriptionImpl.Sync analogously to
   133  // file_operations::fsync == NULL in Linux.
   134  func (FileDescriptionDefaultImpl) Sync(ctx context.Context) error {
   135  	return linuxerr.EINVAL
   136  }
   137  
   138  // ConfigureMMap implements FileDescriptionImpl.ConfigureMMap analogously to
   139  // file_operations::mmap == NULL in Linux.
   140  func (FileDescriptionDefaultImpl) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
   141  	return linuxerr.ENODEV
   142  }
   143  
   144  // Ioctl implements FileDescriptionImpl.Ioctl analogously to
   145  // file_operations::unlocked_ioctl == NULL in Linux.
   146  func (FileDescriptionDefaultImpl) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
   147  	return 0, linuxerr.ENOTTY
   148  }
   149  
   150  // ListXattr implements FileDescriptionImpl.ListXattr analogously to
   151  // inode_operations::listxattr == NULL in Linux.
   152  func (FileDescriptionDefaultImpl) ListXattr(ctx context.Context, size uint64) ([]string, error) {
   153  	// This isn't exactly accurate; see FileDescription.ListXattr.
   154  	return nil, linuxerr.ENOTSUP
   155  }
   156  
   157  // GetXattr implements FileDescriptionImpl.GetXattr analogously to
   158  // inode::i_opflags & IOP_XATTR == 0 in Linux.
   159  func (FileDescriptionDefaultImpl) GetXattr(ctx context.Context, opts GetXattrOptions) (string, error) {
   160  	return "", linuxerr.ENOTSUP
   161  }
   162  
   163  // SetXattr implements FileDescriptionImpl.SetXattr analogously to
   164  // inode::i_opflags & IOP_XATTR == 0 in Linux.
   165  func (FileDescriptionDefaultImpl) SetXattr(ctx context.Context, opts SetXattrOptions) error {
   166  	return linuxerr.ENOTSUP
   167  }
   168  
   169  // RemoveXattr implements FileDescriptionImpl.RemoveXattr analogously to
   170  // inode::i_opflags & IOP_XATTR == 0 in Linux.
   171  func (FileDescriptionDefaultImpl) RemoveXattr(ctx context.Context, name string) error {
   172  	return linuxerr.ENOTSUP
   173  }
   174  
   175  // RegisterFileAsyncHandler implements FileDescriptionImpl.RegisterFileAsyncHandler.
   176  func (FileDescriptionDefaultImpl) RegisterFileAsyncHandler(fd *FileDescription) error {
   177  	return fd.asyncHandler.Register(fd)
   178  }
   179  
   180  // UnregisterFileAsyncHandler implements FileDescriptionImpl.UnregisterFileAsyncHandler.
   181  func (FileDescriptionDefaultImpl) UnregisterFileAsyncHandler(fd *FileDescription) {
   182  	fd.asyncHandler.Unregister(fd)
   183  }
   184  
   185  // DirectoryFileDescriptionDefaultImpl may be embedded by implementations of
   186  // FileDescriptionImpl that always represent directories to obtain
   187  // implementations of non-directory I/O methods that return EISDIR.
   188  //
   189  // +stateify savable
   190  type DirectoryFileDescriptionDefaultImpl struct{}
   191  
   192  // Allocate implements DirectoryFileDescriptionDefaultImpl.Allocate.
   193  func (DirectoryFileDescriptionDefaultImpl) Allocate(ctx context.Context, mode, offset, length uint64) error {
   194  	return linuxerr.EISDIR
   195  }
   196  
   197  // PRead implements FileDescriptionImpl.PRead.
   198  func (DirectoryFileDescriptionDefaultImpl) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
   199  	return 0, linuxerr.EISDIR
   200  }
   201  
   202  // Read implements FileDescriptionImpl.Read.
   203  func (DirectoryFileDescriptionDefaultImpl) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
   204  	return 0, linuxerr.EISDIR
   205  }
   206  
   207  // PWrite implements FileDescriptionImpl.PWrite.
   208  func (DirectoryFileDescriptionDefaultImpl) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
   209  	return 0, linuxerr.EISDIR
   210  }
   211  
   212  // Write implements FileDescriptionImpl.Write.
   213  func (DirectoryFileDescriptionDefaultImpl) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
   214  	return 0, linuxerr.EISDIR
   215  }
   216  
   217  // DentryMetadataFileDescriptionImpl may be embedded by implementations of
   218  // FileDescriptionImpl for which FileDescriptionOptions.UseDentryMetadata is
   219  // true to obtain implementations of Stat and SetStat that panic.
   220  //
   221  // +stateify savable
   222  type DentryMetadataFileDescriptionImpl struct{}
   223  
   224  // Stat implements FileDescriptionImpl.Stat.
   225  func (DentryMetadataFileDescriptionImpl) Stat(ctx context.Context, opts StatOptions) (linux.Statx, error) {
   226  	panic("illegal call to DentryMetadataFileDescriptionImpl.Stat")
   227  }
   228  
   229  // SetStat implements FileDescriptionImpl.SetStat.
   230  func (DentryMetadataFileDescriptionImpl) SetStat(ctx context.Context, opts SetStatOptions) error {
   231  	panic("illegal call to DentryMetadataFileDescriptionImpl.SetStat")
   232  }
   233  
   234  // DynamicBytesSource represents a data source for a
   235  // DynamicBytesFileDescriptionImpl.
   236  //
   237  // +stateify savable
   238  type DynamicBytesSource interface {
   239  	// Generate writes the file's contents to buf.
   240  	Generate(ctx context.Context, buf *bytes.Buffer) error
   241  }
   242  
   243  // StaticData implements DynamicBytesSource over a static string.
   244  //
   245  // +stateify savable
   246  type StaticData struct {
   247  	Data string
   248  }
   249  
   250  // Generate implements DynamicBytesSource.
   251  func (s *StaticData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   252  	buf.WriteString(s.Data)
   253  	return nil
   254  }
   255  
   256  // WritableDynamicBytesSource extends DynamicBytesSource to allow writes to the
   257  // underlying source.
   258  //
   259  // TODO(b/179825241): Make utility for integer-based writable files.
   260  type WritableDynamicBytesSource interface {
   261  	DynamicBytesSource
   262  
   263  	// Write sends writes to the source.
   264  	Write(ctx context.Context, fd *FileDescription, src usermem.IOSequence, offset int64) (int64, error)
   265  }
   266  
   267  // DynamicBytesFileDescriptionImpl may be embedded by implementations of
   268  // FileDescriptionImpl that represent read-only regular files whose contents
   269  // are backed by a bytes.Buffer that is regenerated when necessary, consistent
   270  // with Linux's fs/seq_file.c:single_open().
   271  //
   272  // If data additionally implements WritableDynamicBytesSource, writes are
   273  // dispatched to the implementer. The source data is not automatically modified.
   274  //
   275  // DynamicBytesFileDescriptionImpl.Init() must be called before first
   276  // use.
   277  //
   278  // +stateify savable
   279  type DynamicBytesFileDescriptionImpl struct {
   280  	vfsfd    *FileDescription   // immutable
   281  	data     DynamicBytesSource // immutable
   282  	mu       sync.Mutex         `state:"nosave"` // protects the following fields
   283  	buf      bytes.Buffer       `state:".([]byte)"`
   284  	off      int64
   285  	lastRead int64 // offset at which the last Read, PRead, or Seek ended
   286  }
   287  
   288  func (fd *DynamicBytesFileDescriptionImpl) saveBuf() []byte {
   289  	return fd.buf.Bytes()
   290  }
   291  
   292  func (fd *DynamicBytesFileDescriptionImpl) loadBuf(p []byte) {
   293  	fd.buf.Write(p)
   294  }
   295  
   296  // Init must be called before first use.
   297  func (fd *DynamicBytesFileDescriptionImpl) Init(vfsfd *FileDescription, data DynamicBytesSource) {
   298  	fd.vfsfd = vfsfd
   299  	fd.data = data
   300  }
   301  
   302  // Preconditions: fd.mu must be locked.
   303  func (fd *DynamicBytesFileDescriptionImpl) preadLocked(ctx context.Context, dst usermem.IOSequence, offset int64, opts *ReadOptions) (int64, error) {
   304  	// Regenerate the buffer if it's empty, or before pread() at a new offset.
   305  	// Compare fs/seq_file.c:seq_read() => traverse().
   306  	switch {
   307  	case offset != fd.lastRead:
   308  		fd.buf.Reset()
   309  		fallthrough
   310  	case fd.buf.Len() == 0:
   311  		if err := fd.data.Generate(ctx, &fd.buf); err != nil {
   312  			fd.buf.Reset()
   313  			// fd.off is not updated in this case.
   314  			fd.lastRead = 0
   315  			return 0, err
   316  		}
   317  	}
   318  	bs := fd.buf.Bytes()
   319  	if offset >= int64(len(bs)) {
   320  		return 0, io.EOF
   321  	}
   322  	n, err := dst.CopyOut(ctx, bs[offset:])
   323  	fd.lastRead = offset + int64(n)
   324  	return int64(n), err
   325  }
   326  
   327  // PRead implements FileDescriptionImpl.PRead.
   328  func (fd *DynamicBytesFileDescriptionImpl) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
   329  	fd.mu.Lock()
   330  	n, err := fd.preadLocked(ctx, dst, offset, &opts)
   331  	fd.mu.Unlock()
   332  	return n, err
   333  }
   334  
   335  // Read implements FileDescriptionImpl.Read.
   336  func (fd *DynamicBytesFileDescriptionImpl) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
   337  	fd.mu.Lock()
   338  	n, err := fd.preadLocked(ctx, dst, fd.off, &opts)
   339  	fd.off += n
   340  	fd.mu.Unlock()
   341  	return n, err
   342  }
   343  
   344  // Seek implements FileDescriptionImpl.Seek.
   345  func (fd *DynamicBytesFileDescriptionImpl) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
   346  	fd.mu.Lock()
   347  	defer fd.mu.Unlock()
   348  	switch whence {
   349  	case linux.SEEK_SET:
   350  		// Use offset as given.
   351  	case linux.SEEK_CUR:
   352  		offset += fd.off
   353  	default:
   354  		// fs/seq_file:seq_lseek() rejects SEEK_END etc.
   355  		return 0, linuxerr.EINVAL
   356  	}
   357  	if offset < 0 {
   358  		return 0, linuxerr.EINVAL
   359  	}
   360  	if offset != fd.lastRead {
   361  		// Regenerate the file's contents immediately. Compare
   362  		// fs/seq_file.c:seq_lseek() => traverse().
   363  		fd.buf.Reset()
   364  		if err := fd.data.Generate(ctx, &fd.buf); err != nil {
   365  			fd.buf.Reset()
   366  			fd.off = 0
   367  			fd.lastRead = 0
   368  			return 0, err
   369  		}
   370  		fd.lastRead = offset
   371  	}
   372  	fd.off = offset
   373  	return offset, nil
   374  }
   375  
   376  // Preconditions: fd.mu must be locked.
   377  func (fd *DynamicBytesFileDescriptionImpl) pwriteLocked(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
   378  	if opts.Flags&^(linux.RWF_HIPRI|linux.RWF_DSYNC|linux.RWF_SYNC) != 0 {
   379  		return 0, linuxerr.EOPNOTSUPP
   380  	}
   381  	limit, err := CheckLimit(ctx, offset, src.NumBytes())
   382  	if err != nil {
   383  		return 0, err
   384  	}
   385  	src = src.TakeFirst64(limit)
   386  
   387  	writable, ok := fd.data.(WritableDynamicBytesSource)
   388  	if !ok {
   389  		return 0, linuxerr.EIO
   390  	}
   391  	n, err := writable.Write(ctx, fd.vfsfd, src, offset)
   392  	if err != nil {
   393  		return 0, err
   394  	}
   395  
   396  	// Invalidate cached data that might exist prior to this call.
   397  	fd.buf.Reset()
   398  	return n, nil
   399  }
   400  
   401  // PWrite implements FileDescriptionImpl.PWrite.
   402  func (fd *DynamicBytesFileDescriptionImpl) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
   403  	fd.mu.Lock()
   404  	n, err := fd.pwriteLocked(ctx, src, offset, opts)
   405  	fd.mu.Unlock()
   406  	return n, err
   407  }
   408  
   409  // Write implements FileDescriptionImpl.Write.
   410  func (fd *DynamicBytesFileDescriptionImpl) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
   411  	fd.mu.Lock()
   412  	n, err := fd.pwriteLocked(ctx, src, fd.off, opts)
   413  	fd.off += n
   414  	fd.mu.Unlock()
   415  	return n, err
   416  }
   417  
   418  // GenericConfigureMMap may be used by most implementations of
   419  // FileDescriptionImpl.ConfigureMMap.
   420  func GenericConfigureMMap(fd *FileDescription, m memmap.Mappable, opts *memmap.MMapOpts) error {
   421  	if opts.Offset+opts.Length > math.MaxInt64 {
   422  		return linuxerr.EOVERFLOW
   423  	}
   424  	opts.Mappable = m
   425  	opts.MappingIdentity = fd
   426  	fd.IncRef()
   427  	return nil
   428  }
   429  
   430  // LockFD may be used by most implementations of FileDescriptionImpl.Lock*
   431  // functions. Caller must call Init().
   432  //
   433  // +stateify savable
   434  type LockFD struct {
   435  	locks *FileLocks
   436  }
   437  
   438  // SupportsLocks implements FileDescriptionImpl.SupportsLocks.
   439  func (LockFD) SupportsLocks() bool {
   440  	return true
   441  }
   442  
   443  // Init initializes fd with FileLocks to use.
   444  func (fd *LockFD) Init(locks *FileLocks) {
   445  	fd.locks = locks
   446  }
   447  
   448  // Locks returns the locks associated with this file.
   449  func (fd *LockFD) Locks() *FileLocks {
   450  	return fd.locks
   451  }
   452  
   453  // LockBSD implements FileDescriptionImpl.LockBSD.
   454  func (fd *LockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block bool) error {
   455  	return fd.locks.LockBSD(ctx, uid, ownerPID, t, block)
   456  }
   457  
   458  // UnlockBSD implements FileDescriptionImpl.UnlockBSD.
   459  func (fd *LockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error {
   460  	fd.locks.UnlockBSD(uid)
   461  	return nil
   462  }
   463  
   464  // LockPOSIX implements FileDescriptionImpl.LockPOSIX.
   465  func (fd *LockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block bool) error {
   466  	return fd.locks.LockPOSIX(ctx, uid, ownerPID, t, r, block)
   467  }
   468  
   469  // UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX.
   470  func (fd *LockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error {
   471  	return fd.locks.UnlockPOSIX(ctx, uid, r)
   472  }
   473  
   474  // TestPOSIX implements FileDescriptionImpl.TestPOSIX.
   475  func (fd *LockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
   476  	return fd.locks.TestPOSIX(ctx, uid, t, r)
   477  }
   478  
   479  // NoAsyncEventFD implements [Un]RegisterFileAsyncHandler of FileDescriptionImpl.
   480  type NoAsyncEventFD struct{}
   481  
   482  // RegisterFileAsyncHandler implements FileDescriptionImpl.RegisterFileAsyncHandler.
   483  func (NoAsyncEventFD) RegisterFileAsyncHandler(fd *FileDescription) error {
   484  	return nil
   485  }
   486  
   487  // UnregisterFileAsyncHandler implements FileDescriptionImpl.UnregisterFileAsyncHandler.
   488  func (NoAsyncEventFD) UnregisterFileAsyncHandler(fd *FileDescription) {
   489  }
   490  
   491  // NoLockFD implements Lock*/Unlock* portion of FileDescriptionImpl interface
   492  // returning ENOLCK.
   493  //
   494  // +stateify savable
   495  type NoLockFD struct{}
   496  
   497  // SupportsLocks implements FileDescriptionImpl.SupportsLocks.
   498  func (NoLockFD) SupportsLocks() bool {
   499  	return false
   500  }
   501  
   502  // LockBSD implements FileDescriptionImpl.LockBSD.
   503  func (NoLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block bool) error {
   504  	return linuxerr.ENOLCK
   505  }
   506  
   507  // UnlockBSD implements FileDescriptionImpl.UnlockBSD.
   508  func (NoLockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error {
   509  	return linuxerr.ENOLCK
   510  }
   511  
   512  // LockPOSIX implements FileDescriptionImpl.LockPOSIX.
   513  func (NoLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block bool) error {
   514  	return linuxerr.ENOLCK
   515  }
   516  
   517  // UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX.
   518  func (NoLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error {
   519  	return linuxerr.ENOLCK
   520  }
   521  
   522  // TestPOSIX implements FileDescriptionImpl.TestPOSIX.
   523  func (NoLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
   524  	return linux.Flock{}, linuxerr.ENOLCK
   525  }
   526  
   527  // BadLockFD implements Lock*/Unlock* portion of FileDescriptionImpl interface
   528  // returning EBADF.
   529  //
   530  // +stateify savable
   531  type BadLockFD struct{}
   532  
   533  // SupportsLocks implements FileDescriptionImpl.SupportsLocks.
   534  func (BadLockFD) SupportsLocks() bool {
   535  	return false
   536  }
   537  
   538  // LockBSD implements FileDescriptionImpl.LockBSD.
   539  func (BadLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block bool) error {
   540  	return linuxerr.EBADF
   541  }
   542  
   543  // UnlockBSD implements FileDescriptionImpl.UnlockBSD.
   544  func (BadLockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error {
   545  	return linuxerr.EBADF
   546  }
   547  
   548  // LockPOSIX implements FileDescriptionImpl.LockPOSIX.
   549  func (BadLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block bool) error {
   550  	return linuxerr.EBADF
   551  }
   552  
   553  // UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX.
   554  func (BadLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error {
   555  	return linuxerr.EBADF
   556  }
   557  
   558  // TestPOSIX implements FileDescriptionImpl.TestPOSIX.
   559  func (BadLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
   560  	return linux.Flock{}, linuxerr.EBADF
   561  }