1// Copyright 2021 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package unix
  6
  7import "unsafe"
  8
  9// IoctlRetInt performs an ioctl operation specified by req on a device
 10// associated with opened file descriptor fd, and returns a non-negative
 11// integer that is returned by the ioctl syscall.
 12func IoctlRetInt(fd int, req uint) (int, error) {
 13	ret, _, err := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), 0)
 14	if err != 0 {
 15		return 0, err
 16	}
 17	return int(ret), nil
 18}
 19
 20func IoctlGetUint32(fd int, req uint) (uint32, error) {
 21	var value uint32
 22	err := ioctlPtr(fd, req, unsafe.Pointer(&value))
 23	return value, err
 24}
 25
 26func IoctlGetRTCTime(fd int) (*RTCTime, error) {
 27	var value RTCTime
 28	err := ioctlPtr(fd, RTC_RD_TIME, unsafe.Pointer(&value))
 29	return &value, err
 30}
 31
 32func IoctlSetRTCTime(fd int, value *RTCTime) error {
 33	return ioctlPtr(fd, RTC_SET_TIME, unsafe.Pointer(value))
 34}
 35
 36func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) {
 37	var value RTCWkAlrm
 38	err := ioctlPtr(fd, RTC_WKALM_RD, unsafe.Pointer(&value))
 39	return &value, err
 40}
 41
 42func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error {
 43	return ioctlPtr(fd, RTC_WKALM_SET, unsafe.Pointer(value))
 44}
 45
 46// IoctlGetEthtoolDrvinfo fetches ethtool driver information for the network
 47// device specified by ifname.
 48func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) {
 49	ifr, err := NewIfreq(ifname)
 50	if err != nil {
 51		return nil, err
 52	}
 53
 54	value := EthtoolDrvinfo{Cmd: ETHTOOL_GDRVINFO}
 55	ifrd := ifr.withData(unsafe.Pointer(&value))
 56
 57	err = ioctlIfreqData(fd, SIOCETHTOOL, &ifrd)
 58	return &value, err
 59}
 60
 61// IoctlGetWatchdogInfo fetches information about a watchdog device from the
 62// Linux watchdog API. For more information, see:
 63// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html.
 64func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) {
 65	var value WatchdogInfo
 66	err := ioctlPtr(fd, WDIOC_GETSUPPORT, unsafe.Pointer(&value))
 67	return &value, err
 68}
 69
 70// IoctlWatchdogKeepalive issues a keepalive ioctl to a watchdog device. For
 71// more information, see:
 72// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html.
 73func IoctlWatchdogKeepalive(fd int) error {
 74	// arg is ignored and not a pointer, so ioctl is fine instead of ioctlPtr.
 75	return ioctl(fd, WDIOC_KEEPALIVE, 0)
 76}
 77
 78// IoctlFileCloneRange performs an FICLONERANGE ioctl operation to clone the
 79// range of data conveyed in value to the file associated with the file
 80// descriptor destFd. See the ioctl_ficlonerange(2) man page for details.
 81func IoctlFileCloneRange(destFd int, value *FileCloneRange) error {
 82	return ioctlPtr(destFd, FICLONERANGE, unsafe.Pointer(value))
 83}
 84
 85// IoctlFileClone performs an FICLONE ioctl operation to clone the entire file
 86// associated with the file description srcFd to the file associated with the
 87// file descriptor destFd. See the ioctl_ficlone(2) man page for details.
 88func IoctlFileClone(destFd, srcFd int) error {
 89	return ioctl(destFd, FICLONE, uintptr(srcFd))
 90}
 91
 92type FileDedupeRange struct {
 93	Src_offset uint64
 94	Src_length uint64
 95	Reserved1  uint16
 96	Reserved2  uint32
 97	Info       []FileDedupeRangeInfo
 98}
 99
100type FileDedupeRangeInfo struct {
101	Dest_fd       int64
102	Dest_offset   uint64
103	Bytes_deduped uint64
104	Status        int32
105	Reserved      uint32
106}
107
108// IoctlFileDedupeRange performs an FIDEDUPERANGE ioctl operation to share the
109// range of data conveyed in value from the file associated with the file
110// descriptor srcFd to the value.Info destinations. See the
111// ioctl_fideduperange(2) man page for details.
112func IoctlFileDedupeRange(srcFd int, value *FileDedupeRange) error {
113	buf := make([]byte, SizeofRawFileDedupeRange+
114		len(value.Info)*SizeofRawFileDedupeRangeInfo)
115	rawrange := (*RawFileDedupeRange)(unsafe.Pointer(&buf[0]))
116	rawrange.Src_offset = value.Src_offset
117	rawrange.Src_length = value.Src_length
118	rawrange.Dest_count = uint16(len(value.Info))
119	rawrange.Reserved1 = value.Reserved1
120	rawrange.Reserved2 = value.Reserved2
121
122	for i := range value.Info {
123		rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer(
124			uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) +
125				uintptr(i*SizeofRawFileDedupeRangeInfo)))
126		rawinfo.Dest_fd = value.Info[i].Dest_fd
127		rawinfo.Dest_offset = value.Info[i].Dest_offset
128		rawinfo.Bytes_deduped = value.Info[i].Bytes_deduped
129		rawinfo.Status = value.Info[i].Status
130		rawinfo.Reserved = value.Info[i].Reserved
131	}
132
133	err := ioctlPtr(srcFd, FIDEDUPERANGE, unsafe.Pointer(&buf[0]))
134
135	// Output
136	for i := range value.Info {
137		rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer(
138			uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) +
139				uintptr(i*SizeofRawFileDedupeRangeInfo)))
140		value.Info[i].Dest_fd = rawinfo.Dest_fd
141		value.Info[i].Dest_offset = rawinfo.Dest_offset
142		value.Info[i].Bytes_deduped = rawinfo.Bytes_deduped
143		value.Info[i].Status = rawinfo.Status
144		value.Info[i].Reserved = rawinfo.Reserved
145	}
146
147	return err
148}
149
150func IoctlHIDGetDesc(fd int, value *HIDRawReportDescriptor) error {
151	return ioctlPtr(fd, HIDIOCGRDESC, unsafe.Pointer(value))
152}
153
154func IoctlHIDGetRawInfo(fd int) (*HIDRawDevInfo, error) {
155	var value HIDRawDevInfo
156	err := ioctlPtr(fd, HIDIOCGRAWINFO, unsafe.Pointer(&value))
157	return &value, err
158}
159
160func IoctlHIDGetRawName(fd int) (string, error) {
161	var value [_HIDIOCGRAWNAME_LEN]byte
162	err := ioctlPtr(fd, _HIDIOCGRAWNAME, unsafe.Pointer(&value[0]))
163	return ByteSliceToString(value[:]), err
164}
165
166func IoctlHIDGetRawPhys(fd int) (string, error) {
167	var value [_HIDIOCGRAWPHYS_LEN]byte
168	err := ioctlPtr(fd, _HIDIOCGRAWPHYS, unsafe.Pointer(&value[0]))
169	return ByteSliceToString(value[:]), err
170}
171
172func IoctlHIDGetRawUniq(fd int) (string, error) {
173	var value [_HIDIOCGRAWUNIQ_LEN]byte
174	err := ioctlPtr(fd, _HIDIOCGRAWUNIQ, unsafe.Pointer(&value[0]))
175	return ByteSliceToString(value[:]), err
176}
177
178// IoctlIfreq performs an ioctl using an Ifreq structure for input and/or
179// output. See the netdevice(7) man page for details.
180func IoctlIfreq(fd int, req uint, value *Ifreq) error {
181	// It is possible we will add more fields to *Ifreq itself later to prevent
182	// misuse, so pass the raw *ifreq directly.
183	return ioctlPtr(fd, req, unsafe.Pointer(&value.raw))
184}
185
186// TODO(mdlayher): export if and when IfreqData is exported.
187
188// ioctlIfreqData performs an ioctl using an ifreqData structure for input
189// and/or output. See the netdevice(7) man page for details.
190func ioctlIfreqData(fd int, req uint, value *ifreqData) error {
191	// The memory layout of IfreqData (type-safe) and ifreq (not type-safe) are
192	// identical so pass *IfreqData directly.
193	return ioctlPtr(fd, req, unsafe.Pointer(value))
194}
195
196// IoctlKCMClone attaches a new file descriptor to a multiplexor by cloning an
197// existing KCM socket, returning a structure containing the file descriptor of
198// the new socket.
199func IoctlKCMClone(fd int) (*KCMClone, error) {
200	var info KCMClone
201	if err := ioctlPtr(fd, SIOCKCMCLONE, unsafe.Pointer(&info)); err != nil {
202		return nil, err
203	}
204
205	return &info, nil
206}
207
208// IoctlKCMAttach attaches a TCP socket and associated BPF program file
209// descriptor to a multiplexor.
210func IoctlKCMAttach(fd int, info KCMAttach) error {
211	return ioctlPtr(fd, SIOCKCMATTACH, unsafe.Pointer(&info))
212}
213
214// IoctlKCMUnattach unattaches a TCP socket file descriptor from a multiplexor.
215func IoctlKCMUnattach(fd int, info KCMUnattach) error {
216	return ioctlPtr(fd, SIOCKCMUNATTACH, unsafe.Pointer(&info))
217}
218
219// IoctlLoopGetStatus64 gets the status of the loop device associated with the
220// file descriptor fd using the LOOP_GET_STATUS64 operation.
221func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) {
222	var value LoopInfo64
223	if err := ioctlPtr(fd, LOOP_GET_STATUS64, unsafe.Pointer(&value)); err != nil {
224		return nil, err
225	}
226	return &value, nil
227}
228
229// IoctlLoopSetStatus64 sets the status of the loop device associated with the
230// file descriptor fd using the LOOP_SET_STATUS64 operation.
231func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error {
232	return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value))
233}