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 }