I am reading some Go code (from https://github.com/KarpelesLab/reflink) which is using SyscallConn()
(the func defined on *os.File
):
// reflinkInternal performs the actual reflink action without worrying about fallback
func reflinkInternal(d, s *os.File) error {
ss, err := s.SyscallConn()
if err != nil {
return err
}
sd, err := d.SyscallConn()
if err != nil {
return err
}
var err2, err3 error
err = sd.Control(func(dfd uintptr) {
err2 = ss.Control(func(sfd uintptr) {
// int ioctl(int dest_fd, FICLONE, int src_fd);
err3 = unix.IoctlFileClone(int(dfd), int(sfd))
})
})
if err != nil {
// sd.Control failed
return err
}
if err2 != nil {
// ss.Control failed
return err2
}
if err3 != nil && errors.Is(err3, unix.ENOTSUP) {
return ErrReflinkFailed
}
// err3 is ioctl() response
return err3
}
In this example, are there any advantages to using these Control()
funcs instead of just working with d.Fd()
and s.Fd()
directly? More generally, what is the func func (*os.File) SyscallConn() (syscall.RawConn, error)
useful for?
file.Fd()
returns a file descriptor AND it makes the file descriptor operate in a blocking mode (tying up a thread for blocking operations). SyscallConn
does not do so. It was made in fact explicitly for just getting the file descriptor without making it blocking. See this issue for more information.