xref: /cloud-hypervisor/scripts/check-image-compatibility.sh (revision 3f8cd52ffd74627242cb7e8ea1c2bdedadf6741a)
1#!/usr/bin/env bash
2: '
3    This script checks if an image is compatible with Cloud Hypervisor.
4    At first, it detects the image type(raw or qcow2),
5    partition type whether it is DOS or GPT.
6    Then it mounts the image and checks if VIRTIO Configs
7    are enabled in the kernel config. In the end, it provides
8    a message about the compatibility of the image.
9'
10
11usage="$(basename "$0") [-h] -f -w -- program to check Cloud Hypervisor compatible image
12
13where:
14    -h  show this help text
15    -f  image file location
16    -w  directory to be used for temporary files"
17
18function check_command {
19    if ! command -v "$1" &>/dev/null; then
20        echo "Command $1 could not be found"
21        exit 1
22    fi
23}
24
25function check_if_root {
26    if [ "$EUID" -ne 0 ]; then
27        echo "Please run as root"
28        exit 1
29    fi
30
31}
32
33check_if_root
34working_dir=""
35while getopts ':hf:w:' option; do
36    case "$option" in
37    h)
38        echo "$usage"
39        exit
40        ;;
41    f)
42        file_name=$OPTARG
43        ;;
44    w)
45        working_dir=$OPTARG
46        ;;
47    :)
48        printf "missing argument for -%s\n" "$OPTARG" >&2
49        echo "$usage" >&2
50        exit 1
51        ;;
52    \?)
53        printf "illegal option: -%s\n" "$OPTARG" >&2
54        echo "$usage" >&2
55        exit 1
56        ;;
57    esac
58done
59
60shift $((OPTIND - 1))
61
62if [ -z "${file_name}" ]; then
63    echo "You must provide the image file name"
64    exit 1
65fi
66if [[ ! -f ${file_name} ]]; then
67    echo "File ${file_name} does not exist"
68    exit 1
69fi
70
71file_abs_path=$(readlink -m "${file_name}")
72if [[ "${working_dir}" != "" && ! -d "${working_dir}" ]]; then
73    echo "Directory ${working_dir} does not exist"
74    exit 1
75elif [[ "${working_dir}" == "" ]]; then
76    working_dir=$(mktemp -d)
77    tmp_created=1
78else
79    working_dir=$(readlink -m "${working_dir}")
80fi
81
82filename="${file_name%.*}"
83dest_file=${working_dir}/${filename}.raw
84image_type=$(qemu-img info "${file_abs_path}" | grep 'file format:' | awk '{ print $3 }')
85echo "Image type detected as ${image_type}"
86
87if [[ "${image_type}" == "raw" ]]; then
88    dest_file=${file_abs_path}
89elif [[ "$image_type" == "qcow2" ]]; then
90    if lsmod | grep "nbd" &>/dev/null; then
91        echo "Module nbd is loaded!"
92    else
93        echo "Module nbd is not loaded. Trying to load the module"
94
95        if ! modprobe nbd max_part=8; then
96            echo "failed to load nbd module. Exiting"
97            exit 1
98        fi
99    fi
100    check_command qemu-img
101    dest_file=/dev/nbd0
102    qemu-nbd --connect=${dest_file} "${file_abs_path}" --read-only
103fi
104
105check_command blkid
106#get part info
107part_type=$(blkid -o value -s PTTYPE "${dest_file}")
108
109check_command partx
110nr_partitions=$(partx -g "${dest_file}" | wc -l)
111
112check_command fdisk
113out=$(fdisk -l "${dest_file}" --bytes | grep -i -A "${nr_partitions}" 'Device' | tail -n +2)
114
115IFS='
116'
117i=0
118declare -A lines
119for x in $out; do
120    lines[$i]=$x
121    i=$((i + 1))
122done
123
124declare -A partitions
125IFS=' '
126i=0
127ROWS=${#lines[@]}
128
129for line in "${lines[@]}"; do
130    j=0
131    read -a -r str_arr <<<"$line"
132    for val in "${str_arr[@]}"; do
133        if [[ "$val" != "*" ]]; then
134            partitions[$i, $j]=$val
135            j=$((j + 1))
136        fi
137    done
138    i=$((i + 1))
139done
140
141COLUMNS=$j
142START_ADDRESS_INDEX=1
143FILE_SYS_INDEX2=$((COLUMNS - 1))
144FILE_SYS_INDEX1=$((COLUMNS - 2))
145DEVICE_INDEX=0
146# Here we have all the partition info now lets mount and analyze the contents
147for ((i = 0; i < ROWS; i++)); do
148    if [[ "$part_type" == "gpt" && "${partitions[$i, ${FILE_SYS_INDEX1}]}" == "Linux" && "${partitions[$i, ${FILE_SYS_INDEX2}]}" == "filesystem" ]]; then
149        echo "The image has GPT partitions"
150        MOUNT_ROW=$i
151        break
152    elif [[ "$part_type" == "dos" && "${partitions[$i, ${FILE_SYS_INDEX1}]}" == "Linux" && "${partitions[$i, ${FILE_SYS_INDEX2}]}" == "" ]]; then
153        echo "The image has DOS partitions"
154        MOUNT_ROW=$i
155        break
156    fi
157done
158
159start_address=${partitions[${MOUNT_ROW}, ${START_ADDRESS_INDEX}]}
160offset=$((start_address * 512))
161
162MOUNT_DIR=/mnt/clh-img-check/
163rm -rf ${MOUNT_DIR}
164mkdir ${MOUNT_DIR}
165if [[ "${image_type}" == "raw" ]]; then
166    mount -o ro,loop,offset=$offset "${dest_file}" ${MOUNT_DIR}
167elif [[ "${image_type}" == "qcow2" ]]; then
168    mount -o ro "${partitions[${MOUNT_ROW}, ${DEVICE_INDEX}]}" ${MOUNT_DIR}
169fi
170
171CONFIG_DIR=${MOUNT_DIR}boot/
172if [[ "$part_type" == "dos" ]]; then
173    CONFIG_DIR=${MOUNT_DIR}
174fi
175
176#check VIRTIO
177HAS_VIRTIO=1
178for conf_file in "${CONFIG_DIR}"config*; do
179    out=$(grep -cE "CONFIG_VIRTIO=y|CONFIG_VIRTIO_BLK=y|CONFIG_VIRTIO_BLK=m" "${conf_file}")
180    if [[ "$out" != "2" ]]; then
181        echo "VIRTIO not found"
182        HAS_VIRTIO=0
183    fi
184done
185
186#clean up
187umount ${MOUNT_DIR}
188
189if [[ "${tmp_created}" == "1" ]]; then
190    rm -rf "${working_dir}"
191fi
192
193if [[ "${image_type}" == "qcow2" ]]; then
194    qemu-nbd --disconnect "${dest_file}" >/dev/null
195fi
196
197result=""
198if [[ "${part_type}" == "dos" ]]; then
199    result="dos mode not supported"
200fi
201if [[ "${HAS_VIRTIO}" == "0" ]]; then
202    if [[ "$result" != "" ]]; then
203        result="${result},"
204    fi
205    result="$result VirtIO module not found in the image"
206fi
207if [[ "$result" == "" ]]; then
208    echo "No incompatibilities found"
209else
210    echo "$result"
211fi
212