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 }