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}
234
235// IoctlLoopConfigure configures all loop device parameters in a single step
236func IoctlLoopConfigure(fd int, value *LoopConfig) error {
237	return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value))
238}
239