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