1*d87b258bSHyman Huang#!/usr/bin/env python3 2*d87b258bSHyman Huang# group: rw auto 3*d87b258bSHyman Huang# 4*d87b258bSHyman Huang# Test LUKS volume with detached header 5*d87b258bSHyman Huang# 6*d87b258bSHyman Huang# Copyright (C) 2024 SmartX Inc. 7*d87b258bSHyman Huang# 8*d87b258bSHyman Huang# Authors: 9*d87b258bSHyman Huang# Hyman Huang <yong.huang@smartx.com> 10*d87b258bSHyman Huang# 11*d87b258bSHyman Huang# This program is free software; you can redistribute it and/or modify 12*d87b258bSHyman Huang# it under the terms of the GNU General Public License as published by 13*d87b258bSHyman Huang# the Free Software Foundation; either version 2 of the License, or 14*d87b258bSHyman Huang# (at your option) any later version. 15*d87b258bSHyman Huang# 16*d87b258bSHyman Huang# This program is distributed in the hope that it will be useful, 17*d87b258bSHyman Huang# but WITHOUT ANY WARRANTY; without even the implied warranty of 18*d87b258bSHyman Huang# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19*d87b258bSHyman Huang# GNU General Public License for more details. 20*d87b258bSHyman Huang# 21*d87b258bSHyman Huang# You should have received a copy of the GNU General Public License 22*d87b258bSHyman Huang# along with this program. If not, see <http://www.gnu.org/licenses/>. 23*d87b258bSHyman Huang# 24*d87b258bSHyman Huang 25*d87b258bSHyman Huangimport os 26*d87b258bSHyman Huangimport json 27*d87b258bSHyman Huangimport iotests 28*d87b258bSHyman Huangfrom iotests import ( 29*d87b258bSHyman Huang imgfmt, 30*d87b258bSHyman Huang qemu_img_create, 31*d87b258bSHyman Huang qemu_img_info, 32*d87b258bSHyman Huang QMPTestCase, 33*d87b258bSHyman Huang) 34*d87b258bSHyman Huang 35*d87b258bSHyman Huang 36*d87b258bSHyman Huangimage_size = 128 * 1024 * 1024 37*d87b258bSHyman Huang 38*d87b258bSHyman Huangluks_img = os.path.join(iotests.test_dir, "luks.img") 39*d87b258bSHyman Huangdetached_header_img1 = os.path.join(iotests.test_dir, "detached_header.img1") 40*d87b258bSHyman Huangdetached_header_img2 = os.path.join(iotests.test_dir, "detached_header.img2") 41*d87b258bSHyman Huangdetached_payload_raw_img = os.path.join( 42*d87b258bSHyman Huang iotests.test_dir, "detached_payload_raw.img" 43*d87b258bSHyman Huang) 44*d87b258bSHyman Huangdetached_payload_qcow2_img = os.path.join( 45*d87b258bSHyman Huang iotests.test_dir, "detached_payload_qcow2.img" 46*d87b258bSHyman Huang) 47*d87b258bSHyman Huangdetached_header_raw_img = "json:" + json.dumps( 48*d87b258bSHyman Huang { 49*d87b258bSHyman Huang "driver": "luks", 50*d87b258bSHyman Huang "file": {"filename": detached_payload_raw_img}, 51*d87b258bSHyman Huang "header": { 52*d87b258bSHyman Huang "filename": detached_header_img1, 53*d87b258bSHyman Huang }, 54*d87b258bSHyman Huang } 55*d87b258bSHyman Huang) 56*d87b258bSHyman Huangdetached_header_qcow2_img = "json:" + json.dumps( 57*d87b258bSHyman Huang { 58*d87b258bSHyman Huang "driver": "luks", 59*d87b258bSHyman Huang "file": {"filename": detached_payload_qcow2_img}, 60*d87b258bSHyman Huang "header": {"filename": detached_header_img2}, 61*d87b258bSHyman Huang } 62*d87b258bSHyman Huang) 63*d87b258bSHyman Huang 64*d87b258bSHyman Huangsecret_obj = "secret,id=sec0,data=foo" 65*d87b258bSHyman Huangluks_opts = "key-secret=sec0" 66*d87b258bSHyman Huang 67*d87b258bSHyman Huang 68*d87b258bSHyman Huangclass TestDetachedLUKSHeader(QMPTestCase): 69*d87b258bSHyman Huang def setUp(self) -> None: 70*d87b258bSHyman Huang self.vm = iotests.VM() 71*d87b258bSHyman Huang self.vm.add_object(secret_obj) 72*d87b258bSHyman Huang self.vm.launch() 73*d87b258bSHyman Huang 74*d87b258bSHyman Huang # 1. Create the normal LUKS disk with 128M size 75*d87b258bSHyman Huang self.vm.blockdev_create( 76*d87b258bSHyman Huang {"driver": "file", "filename": luks_img, "size": 0} 77*d87b258bSHyman Huang ) 78*d87b258bSHyman Huang self.vm.qmp_log( 79*d87b258bSHyman Huang "blockdev-add", 80*d87b258bSHyman Huang driver="file", 81*d87b258bSHyman Huang filename=luks_img, 82*d87b258bSHyman Huang node_name="luks-1-storage", 83*d87b258bSHyman Huang ) 84*d87b258bSHyman Huang result = self.vm.blockdev_create( 85*d87b258bSHyman Huang { 86*d87b258bSHyman Huang "driver": imgfmt, 87*d87b258bSHyman Huang "file": "luks-1-storage", 88*d87b258bSHyman Huang "key-secret": "sec0", 89*d87b258bSHyman Huang "size": image_size, 90*d87b258bSHyman Huang "iter-time": 10, 91*d87b258bSHyman Huang } 92*d87b258bSHyman Huang ) 93*d87b258bSHyman Huang # None is expected 94*d87b258bSHyman Huang self.assertEqual(result, None) 95*d87b258bSHyman Huang 96*d87b258bSHyman Huang # 2. Create the LUKS disk with detached header (raw) 97*d87b258bSHyman Huang 98*d87b258bSHyman Huang # Create detached LUKS header 99*d87b258bSHyman Huang self.vm.blockdev_create( 100*d87b258bSHyman Huang {"driver": "file", "filename": detached_header_img1, "size": 0} 101*d87b258bSHyman Huang ) 102*d87b258bSHyman Huang self.vm.qmp_log( 103*d87b258bSHyman Huang "blockdev-add", 104*d87b258bSHyman Huang driver="file", 105*d87b258bSHyman Huang filename=detached_header_img1, 106*d87b258bSHyman Huang node_name="luks-2-header-storage", 107*d87b258bSHyman Huang ) 108*d87b258bSHyman Huang 109*d87b258bSHyman Huang # Create detached LUKS raw payload 110*d87b258bSHyman Huang self.vm.blockdev_create( 111*d87b258bSHyman Huang {"driver": "file", "filename": detached_payload_raw_img, "size": 0} 112*d87b258bSHyman Huang ) 113*d87b258bSHyman Huang self.vm.qmp_log( 114*d87b258bSHyman Huang "blockdev-add", 115*d87b258bSHyman Huang driver="file", 116*d87b258bSHyman Huang filename=detached_payload_raw_img, 117*d87b258bSHyman Huang node_name="luks-2-payload-storage", 118*d87b258bSHyman Huang ) 119*d87b258bSHyman Huang 120*d87b258bSHyman Huang # Format LUKS disk with detached header 121*d87b258bSHyman Huang result = self.vm.blockdev_create( 122*d87b258bSHyman Huang { 123*d87b258bSHyman Huang "driver": imgfmt, 124*d87b258bSHyman Huang "header": "luks-2-header-storage", 125*d87b258bSHyman Huang "file": "luks-2-payload-storage", 126*d87b258bSHyman Huang "key-secret": "sec0", 127*d87b258bSHyman Huang "preallocation": "full", 128*d87b258bSHyman Huang "size": image_size, 129*d87b258bSHyman Huang "iter-time": 10, 130*d87b258bSHyman Huang } 131*d87b258bSHyman Huang ) 132*d87b258bSHyman Huang self.assertEqual(result, None) 133*d87b258bSHyman Huang 134*d87b258bSHyman Huang self.vm.shutdown() 135*d87b258bSHyman Huang 136*d87b258bSHyman Huang # 3. Create the LUKS disk with detached header (qcow2) 137*d87b258bSHyman Huang 138*d87b258bSHyman Huang # Create detached LUKS header using qemu-img 139*d87b258bSHyman Huang res = qemu_img_create( 140*d87b258bSHyman Huang "-f", 141*d87b258bSHyman Huang "luks", 142*d87b258bSHyman Huang "--object", 143*d87b258bSHyman Huang secret_obj, 144*d87b258bSHyman Huang "-o", 145*d87b258bSHyman Huang luks_opts, 146*d87b258bSHyman Huang "-o", 147*d87b258bSHyman Huang "detached-header=true", 148*d87b258bSHyman Huang detached_header_img2, 149*d87b258bSHyman Huang ) 150*d87b258bSHyman Huang assert res.returncode == 0 151*d87b258bSHyman Huang 152*d87b258bSHyman Huang # Create detached LUKS qcow2 payload 153*d87b258bSHyman Huang res = qemu_img_create( 154*d87b258bSHyman Huang "-f", "qcow2", detached_payload_qcow2_img, str(image_size) 155*d87b258bSHyman Huang ) 156*d87b258bSHyman Huang assert res.returncode == 0 157*d87b258bSHyman Huang 158*d87b258bSHyman Huang def tearDown(self) -> None: 159*d87b258bSHyman Huang os.remove(luks_img) 160*d87b258bSHyman Huang os.remove(detached_header_img1) 161*d87b258bSHyman Huang os.remove(detached_header_img2) 162*d87b258bSHyman Huang os.remove(detached_payload_raw_img) 163*d87b258bSHyman Huang os.remove(detached_payload_qcow2_img) 164*d87b258bSHyman Huang 165*d87b258bSHyman Huang # Check if there was any qemu-io run that failed 166*d87b258bSHyman Huang if "Pattern verification failed" in self.vm.get_log(): 167*d87b258bSHyman Huang print("ERROR: Pattern verification failed:") 168*d87b258bSHyman Huang print(self.vm.get_log()) 169*d87b258bSHyman Huang self.fail("qemu-io pattern verification failed") 170*d87b258bSHyman Huang 171*d87b258bSHyman Huang def test_img_creation(self) -> None: 172*d87b258bSHyman Huang # Check if the images created above are expected 173*d87b258bSHyman Huang 174*d87b258bSHyman Huang data = qemu_img_info(luks_img)["format-specific"] 175*d87b258bSHyman Huang self.assertEqual(data["type"], imgfmt) 176*d87b258bSHyman Huang self.assertEqual(data["data"]["detached-header"], False) 177*d87b258bSHyman Huang 178*d87b258bSHyman Huang data = qemu_img_info(detached_header_raw_img)["format-specific"] 179*d87b258bSHyman Huang self.assertEqual(data["type"], imgfmt) 180*d87b258bSHyman Huang self.assertEqual(data["data"]["detached-header"], True) 181*d87b258bSHyman Huang 182*d87b258bSHyman Huang data = qemu_img_info(detached_header_qcow2_img)["format-specific"] 183*d87b258bSHyman Huang self.assertEqual(data["type"], imgfmt) 184*d87b258bSHyman Huang self.assertEqual(data["data"]["detached-header"], True) 185*d87b258bSHyman Huang 186*d87b258bSHyman Huang # Check if preallocation works 187*d87b258bSHyman Huang size = qemu_img_info(detached_payload_raw_img)["actual-size"] 188*d87b258bSHyman Huang self.assertGreaterEqual(size, image_size) 189*d87b258bSHyman Huang 190*d87b258bSHyman Huang def test_detached_luks_header(self) -> None: 191*d87b258bSHyman Huang self.vm.launch() 192*d87b258bSHyman Huang 193*d87b258bSHyman Huang # 1. Add the disk created above 194*d87b258bSHyman Huang 195*d87b258bSHyman Huang # Add normal LUKS disk 196*d87b258bSHyman Huang self.vm.qmp_log( 197*d87b258bSHyman Huang "blockdev-add", 198*d87b258bSHyman Huang driver="file", 199*d87b258bSHyman Huang filename=luks_img, 200*d87b258bSHyman Huang node_name="luks-1-storage", 201*d87b258bSHyman Huang ) 202*d87b258bSHyman Huang result = self.vm.qmp_log( 203*d87b258bSHyman Huang "blockdev-add", 204*d87b258bSHyman Huang driver="luks", 205*d87b258bSHyman Huang file="luks-1-storage", 206*d87b258bSHyman Huang key_secret="sec0", 207*d87b258bSHyman Huang node_name="luks-1-format", 208*d87b258bSHyman Huang ) 209*d87b258bSHyman Huang 210*d87b258bSHyman Huang # Expected result{ "return": {} } 211*d87b258bSHyman Huang self.assert_qmp(result, "return", {}) 212*d87b258bSHyman Huang 213*d87b258bSHyman Huang # Add detached LUKS header with raw payload 214*d87b258bSHyman Huang self.vm.qmp_log( 215*d87b258bSHyman Huang "blockdev-add", 216*d87b258bSHyman Huang driver="file", 217*d87b258bSHyman Huang filename=detached_header_img1, 218*d87b258bSHyman Huang node_name="luks-header1-storage", 219*d87b258bSHyman Huang ) 220*d87b258bSHyman Huang 221*d87b258bSHyman Huang self.vm.qmp_log( 222*d87b258bSHyman Huang "blockdev-add", 223*d87b258bSHyman Huang driver="file", 224*d87b258bSHyman Huang filename=detached_payload_raw_img, 225*d87b258bSHyman Huang node_name="luks-2-payload-raw-storage", 226*d87b258bSHyman Huang ) 227*d87b258bSHyman Huang 228*d87b258bSHyman Huang result = self.vm.qmp_log( 229*d87b258bSHyman Huang "blockdev-add", 230*d87b258bSHyman Huang driver=imgfmt, 231*d87b258bSHyman Huang header="luks-header1-storage", 232*d87b258bSHyman Huang file="luks-2-payload-raw-storage", 233*d87b258bSHyman Huang key_secret="sec0", 234*d87b258bSHyman Huang node_name="luks-2-payload-raw-format", 235*d87b258bSHyman Huang ) 236*d87b258bSHyman Huang self.assert_qmp(result, "return", {}) 237*d87b258bSHyman Huang 238*d87b258bSHyman Huang # Add detached LUKS header with qcow2 payload 239*d87b258bSHyman Huang self.vm.qmp_log( 240*d87b258bSHyman Huang "blockdev-add", 241*d87b258bSHyman Huang driver="file", 242*d87b258bSHyman Huang filename=detached_header_img2, 243*d87b258bSHyman Huang node_name="luks-header2-storage", 244*d87b258bSHyman Huang ) 245*d87b258bSHyman Huang 246*d87b258bSHyman Huang self.vm.qmp_log( 247*d87b258bSHyman Huang "blockdev-add", 248*d87b258bSHyman Huang driver="file", 249*d87b258bSHyman Huang filename=detached_payload_qcow2_img, 250*d87b258bSHyman Huang node_name="luks-3-payload-qcow2-storage", 251*d87b258bSHyman Huang ) 252*d87b258bSHyman Huang 253*d87b258bSHyman Huang result = self.vm.qmp_log( 254*d87b258bSHyman Huang "blockdev-add", 255*d87b258bSHyman Huang driver=imgfmt, 256*d87b258bSHyman Huang header="luks-header2-storage", 257*d87b258bSHyman Huang file="luks-3-payload-qcow2-storage", 258*d87b258bSHyman Huang key_secret="sec0", 259*d87b258bSHyman Huang node_name="luks-3-payload-qcow2-format", 260*d87b258bSHyman Huang ) 261*d87b258bSHyman Huang self.assert_qmp(result, "return", {}) 262*d87b258bSHyman Huang 263*d87b258bSHyman Huang # 2. Do I/O test 264*d87b258bSHyman Huang 265*d87b258bSHyman Huang # Do some I/O to the image to see whether it still works 266*d87b258bSHyman Huang # (Pattern verification will be checked by tearDown()) 267*d87b258bSHyman Huang 268*d87b258bSHyman Huang # Normal LUKS disk 269*d87b258bSHyman Huang result = self.vm.qmp_log( 270*d87b258bSHyman Huang "human-monitor-command", 271*d87b258bSHyman Huang command_line='qemu-io luks-1-format "write -P 40 0 64k"', 272*d87b258bSHyman Huang ) 273*d87b258bSHyman Huang self.assert_qmp(result, "return", "") 274*d87b258bSHyman Huang 275*d87b258bSHyman Huang result = self.vm.qmp_log( 276*d87b258bSHyman Huang "human-monitor-command", 277*d87b258bSHyman Huang command_line='qemu-io luks-1-format "read -P 40 0 64k"', 278*d87b258bSHyman Huang ) 279*d87b258bSHyman Huang self.assert_qmp(result, "return", "") 280*d87b258bSHyman Huang 281*d87b258bSHyman Huang # Detached LUKS header with raw payload 282*d87b258bSHyman Huang cmd = 'qemu-io luks-2-payload-raw-format "write -P 41 0 64k"' 283*d87b258bSHyman Huang result = self.vm.qmp( 284*d87b258bSHyman Huang "human-monitor-command", 285*d87b258bSHyman Huang command_line=cmd 286*d87b258bSHyman Huang ) 287*d87b258bSHyman Huang self.assert_qmp(result, "return", "") 288*d87b258bSHyman Huang 289*d87b258bSHyman Huang cmd = 'qemu-io luks-2-payload-raw-format "read -P 41 0 64k"' 290*d87b258bSHyman Huang result = self.vm.qmp( 291*d87b258bSHyman Huang "human-monitor-command", 292*d87b258bSHyman Huang command_line=cmd 293*d87b258bSHyman Huang ) 294*d87b258bSHyman Huang self.assert_qmp(result, "return", "") 295*d87b258bSHyman Huang 296*d87b258bSHyman Huang # Detached LUKS header with qcow2 payload 297*d87b258bSHyman Huang cmd = 'qemu-io luks-3-payload-qcow2-format "write -P 42 0 64k"' 298*d87b258bSHyman Huang result = self.vm.qmp( 299*d87b258bSHyman Huang "human-monitor-command", 300*d87b258bSHyman Huang command_line=cmd 301*d87b258bSHyman Huang ) 302*d87b258bSHyman Huang self.assert_qmp(result, "return", "") 303*d87b258bSHyman Huang 304*d87b258bSHyman Huang cmd = 'qemu-io luks-3-payload-qcow2-format "read -P 42 0 64k"' 305*d87b258bSHyman Huang result = self.vm.qmp( 306*d87b258bSHyman Huang "human-monitor-command", 307*d87b258bSHyman Huang command_line=cmd 308*d87b258bSHyman Huang ) 309*d87b258bSHyman Huang self.assert_qmp(result, "return", "") 310*d87b258bSHyman Huang 311*d87b258bSHyman Huang self.vm.shutdown() 312*d87b258bSHyman Huang 313*d87b258bSHyman Huang 314*d87b258bSHyman Huangif __name__ == "__main__": 315*d87b258bSHyman Huang # Test image creation and I/O 316*d87b258bSHyman Huang iotests.main(supported_fmts=["luks"], supported_protocols=["file"]) 317