github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/utils/utils_linux.go (about)

     1  // Copyright (c) 2018 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package utils
     7  
     8  import (
     9  	"bufio"
    10  	"crypto/rand"
    11  	"fmt"
    12  	"io"
    13  	"math/big"
    14  	"os"
    15  	"strings"
    16  	"syscall"
    17  	"unsafe"
    18  
    19  	"golang.org/x/sys/unix"
    20  )
    21  
    22  var ioctlFunc = Ioctl
    23  
    24  // maxUInt represents the maximum valid value for the context ID.
    25  // The upper 32 bits of the CID are reserved and zeroed.
    26  // See http://stefanha.github.io/virtio/
    27  var maxUInt uint64 = 1<<32 - 1
    28  
    29  func Ioctl(fd uintptr, request, data uintptr) error {
    30  	if _, _, errno := unix.Syscall(unix.SYS_IOCTL, fd, request, data); errno != 0 {
    31  		//uintptr(request)
    32  		//uintptr(unsafe.Pointer(&arg1)),
    33  		//); errno != 0 {
    34  		return os.NewSyscallError("ioctl", fmt.Errorf("%d", int(errno)))
    35  	}
    36  
    37  	return nil
    38  }
    39  
    40  // FindContextID finds a unique context ID by generating a random number between 3 and max unsigned int (maxUint).
    41  // Using the ioctl VHOST_VSOCK_SET_GUEST_CID, findContextID asks to the kernel if the given
    42  // context ID (N) is available, when the context ID is not available, incrementing by 1 findContextID
    43  // iterates from N to maxUint until an available context ID is found, otherwise decrementing by 1
    44  // findContextID iterates from N to 3 until an available context ID is found, this is the last chance
    45  // to find a context ID available.
    46  // On success vhost file and a context ID greater or equal than 3 are returned, otherwise 0 and an error are returned.
    47  // vhost file can be used to send vhost file decriptor to QEMU. It's the caller's responsibility to
    48  // close vhost file descriptor.
    49  //
    50  // Benefits of using random context IDs:
    51  // - Reduce the probability of a *DoS attack*, since other processes don't know whatis the initial context ID
    52  //   used by findContextID to find a context ID available
    53  //
    54  func FindContextID() (*os.File, uint64, error) {
    55  	// context IDs 0x0, 0x1 and 0x2 are reserved, 0x3 is the first context ID usable.
    56  	var firstContextID uint64 = 0x3
    57  	var contextID = firstContextID
    58  
    59  	// Generate a random number
    60  	n, err := rand.Int(rand.Reader, big.NewInt(int64(maxUInt)))
    61  	if err == nil && n.Int64() >= int64(firstContextID) {
    62  		contextID = uint64(n.Int64())
    63  	}
    64  
    65  	// Open vhost-vsock device to check what context ID is available.
    66  	// This file descriptor holds/locks the context ID and it should be
    67  	// inherited by QEMU process.
    68  	vsockFd, err := os.OpenFile(VHostVSockDevicePath, syscall.O_RDWR, 0666)
    69  	if err != nil {
    70  		return nil, 0, err
    71  	}
    72  
    73  	// Looking for the first available context ID.
    74  	for cid := contextID; cid <= maxUInt; cid++ {
    75  		if err = ioctlFunc(vsockFd.Fd(), ioctlVhostVsockSetGuestCid, uintptr(unsafe.Pointer(&cid))); err == nil {
    76  			return vsockFd, cid, nil
    77  		}
    78  	}
    79  
    80  	ioctlVhostVsockSetGuestCid := getIoctlVhostVsockGuestCid()
    81  	// Last chance to get a free context ID.
    82  	for cid := contextID - 1; cid >= firstContextID; cid-- {
    83  		if err = ioctlFunc(vsockFd.Fd(), ioctlVhostVsockSetGuestCid, uintptr(unsafe.Pointer(&cid))); err == nil {
    84  			return vsockFd, cid, nil
    85  		}
    86  	}
    87  
    88  	vsockFd.Close()
    89  	return nil, 0, fmt.Errorf("Could not get a unique context ID for the vsock : %s", err)
    90  }
    91  
    92  const (
    93  	procMountsFile = "/proc/mounts"
    94  
    95  	fieldsPerLine = 6
    96  )
    97  
    98  const (
    99  	procDeviceIndex = iota
   100  	procPathIndex
   101  	procTypeIndex
   102  	procOptionIndex
   103  )
   104  
   105  // GetDevicePathAndFsTypeOptions gets the device for the mount point, the file system type
   106  // and mount options
   107  func GetDevicePathAndFsTypeOptions(mountPoint string) (devicePath, fsType string, fsOptions []string, err error) {
   108  	if mountPoint == "" {
   109  		err = fmt.Errorf("Mount point cannot be empty")
   110  		return
   111  	}
   112  
   113  	var file *os.File
   114  
   115  	file, err = os.Open(procMountsFile)
   116  	if err != nil {
   117  		return
   118  	}
   119  
   120  	defer file.Close()
   121  
   122  	reader := bufio.NewReader(file)
   123  	for {
   124  		var line string
   125  
   126  		line, err = reader.ReadString('\n')
   127  		if err == io.EOF {
   128  			err = fmt.Errorf("Mount %s not found", mountPoint)
   129  			return
   130  		}
   131  
   132  		fields := strings.Fields(line)
   133  		if len(fields) != fieldsPerLine {
   134  			err = fmt.Errorf("Incorrect no of fields (expected %d, got %d)) :%s", fieldsPerLine, len(fields), line)
   135  			return
   136  		}
   137  
   138  		if mountPoint == fields[procPathIndex] {
   139  			devicePath = fields[procDeviceIndex]
   140  			fsType = fields[procTypeIndex]
   141  			fsOptions = strings.Split(fields[procOptionIndex], ",")
   142  			return
   143  		}
   144  	}
   145  }