fd.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package sys
  2. import (
  3. "fmt"
  4. "math"
  5. "os"
  6. "runtime"
  7. "strconv"
  8. "github.com/cilium/ebpf/internal/unix"
  9. )
  10. var ErrClosedFd = unix.EBADF
  11. type FD struct {
  12. raw int
  13. }
  14. func newFD(value int) *FD {
  15. fd := &FD{value}
  16. runtime.SetFinalizer(fd, (*FD).Close)
  17. return fd
  18. }
  19. // NewFD wraps a raw fd with a finalizer.
  20. //
  21. // You must not use the raw fd after calling this function, since the underlying
  22. // file descriptor number may change. This is because the BPF UAPI assumes that
  23. // zero is not a valid fd value.
  24. func NewFD(value int) (*FD, error) {
  25. if value < 0 {
  26. return nil, fmt.Errorf("invalid fd %d", value)
  27. }
  28. fd := newFD(value)
  29. if value != 0 {
  30. return fd, nil
  31. }
  32. dup, err := fd.Dup()
  33. _ = fd.Close()
  34. return dup, err
  35. }
  36. func (fd *FD) String() string {
  37. return strconv.FormatInt(int64(fd.raw), 10)
  38. }
  39. func (fd *FD) Int() int {
  40. return fd.raw
  41. }
  42. func (fd *FD) Uint() uint32 {
  43. if fd.raw < 0 || int64(fd.raw) > math.MaxUint32 {
  44. // Best effort: this is the number most likely to be an invalid file
  45. // descriptor. It is equal to -1 (on two's complement arches).
  46. return math.MaxUint32
  47. }
  48. return uint32(fd.raw)
  49. }
  50. func (fd *FD) Close() error {
  51. if fd.raw < 0 {
  52. return nil
  53. }
  54. value := int(fd.raw)
  55. fd.raw = -1
  56. fd.Forget()
  57. return unix.Close(value)
  58. }
  59. func (fd *FD) Forget() {
  60. runtime.SetFinalizer(fd, nil)
  61. }
  62. func (fd *FD) Dup() (*FD, error) {
  63. if fd.raw < 0 {
  64. return nil, ErrClosedFd
  65. }
  66. // Always require the fd to be larger than zero: the BPF API treats the value
  67. // as "no argument provided".
  68. dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 1)
  69. if err != nil {
  70. return nil, fmt.Errorf("can't dup fd: %v", err)
  71. }
  72. return newFD(dup), nil
  73. }
  74. func (fd *FD) File(name string) *os.File {
  75. fd.Forget()
  76. return os.NewFile(uintptr(fd.raw), name)
  77. }