package fdutils
import (
"fmt"
"os"
"golang.org/x/sys/unix"
"cyphar.com/go-pathrs/internal/libpathrs"
)
func DupFd(fd uintptr, name string) (*os.File, error) {
newFd, err := unix.FcntlInt(fd, unix.F_DUPFD_CLOEXEC, 0)
if err != nil {
return nil, fmt.Errorf("fcntl(F_DUPFD_CLOEXEC): %w", err)
}
return os.NewFile(uintptr(newFd), name), nil
}
func WithFileFd[T any](file *os.File, fn func(fd uintptr) (T, error)) (T, error) {
conn, err := file.SyscallConn()
if err != nil {
return *new(T), err
}
var (
ret T
innerErr error
)
if err := conn.Control(func(fd uintptr) {
ret, innerErr = fn(fd)
}); err != nil {
return *new(T), err
}
return ret, innerErr
}
func DupFile(file *os.File) (*os.File, error) {
return WithFileFd(file, func(fd uintptr) (*os.File, error) {
return DupFd(fd, file.Name())
})
}
func MkFile(fd uintptr) (*os.File, error) {
fdPath := fmt.Sprintf("fd/%d", fd)
fdName, err := libpathrs.ProcReadlinkat(libpathrs.ProcDefaultRootFd, libpathrs.ProcThreadSelf, fdPath)
if err != nil {
_ = unix.Close(int(fd))
return nil, fmt.Errorf("failed to fetch real name of fd %d: %w", fd, err)
}
return os.NewFile(fd, fdName), nil
}