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