gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/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  )
   103  
   104  // GetDevicePathAndFsType gets the device for the mount point and the file system type
   105  // of the mount.
   106  func GetDevicePathAndFsType(mountPoint string) (devicePath, fsType string, err error) {
   107  	if mountPoint == "" {
   108  		err = fmt.Errorf("Mount point cannot be empty")
   109  		return
   110  	}
   111  
   112  	var file *os.File
   113  
   114  	file, err = os.Open(procMountsFile)
   115  	if err != nil {
   116  		return
   117  	}
   118  
   119  	defer file.Close()
   120  
   121  	reader := bufio.NewReader(file)
   122  	for {
   123  		var line string
   124  
   125  		line, err = reader.ReadString('\n')
   126  		if err == io.EOF {
   127  			err = fmt.Errorf("Mount %s not found", mountPoint)
   128  			return
   129  		}
   130  
   131  		fields := strings.Fields(line)
   132  		if len(fields) != fieldsPerLine {
   133  			err = fmt.Errorf("Incorrect no of fields (expected %d, got %d)) :%s", fieldsPerLine, len(fields), line)
   134  			return
   135  		}
   136  
   137  		if mountPoint == fields[procPathIndex] {
   138  			devicePath = fields[procDeviceIndex]
   139  			fsType = fields[procTypeIndex]
   140  			return
   141  		}
   142  	}
   143  }