12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- import os
- import infra.basetest
- class TestDdrescue(infra.basetest.BRTest):
- # A specific configuration is needed for testing ddrescue:
- # - A kernel config fragment enables loop blk dev and device
- # mapper dm-dust, which are used to simulate a failing storage
- # block device.
- # - dmraid user space package is needed to configure dm-dust
- config = \
- """
- BR2_aarch64=y
- BR2_TOOLCHAIN_EXTERNAL=y
- BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
- BR2_LINUX_KERNEL=y
- BR2_LINUX_KERNEL_CUSTOM_VERSION=y
- BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.15"
- BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
- BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
- BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
- BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
- BR2_PACKAGE_DDRESCUE=y
- BR2_PACKAGE_DMRAID=y
- BR2_TARGET_ROOTFS_CPIO=y
- BR2_TARGET_ROOTFS_CPIO_GZIP=y
- # BR2_TARGET_ROOTFS_TAR is not set
- """.format(
- infra.filepath("tests/package/test_ddrescue/linux-ddrescue.fragment")
- )
- def test_run(self):
- img = os.path.join(self.builddir, "images", "rootfs.cpio.gz")
- kern = os.path.join(self.builddir, "images", "Image")
- self.emulator.boot(arch="aarch64",
- kernel=kern,
- kernel_cmdline=["console=ttyAMA0"],
- options=["-M", "virt", "-cpu", "cortex-a57", "-m", "256M", "-initrd", img])
- self.emulator.login()
- # Test variables:
- dev_img = "/tmp/dev.img"
- lo_dev = "/dev/loop0"
- dm_dev_name = "dust0"
- dm_dev = f"/dev/mapper/{dm_dev_name}"
- ddrescue_img = "/tmp/ddrescue.img"
- # Test the program can execute
- self.assertRunOk("ddrescue --version")
- # Create a 1MB file of zeroes for initial loopback block device
- self.assertRunOk(f"dd if=/dev/zero of={dev_img} bs=1M count=1")
- # Setup lookback block device
- self.assertRunOk(f"losetup {lo_dev} {dev_img}")
- # Create and setup dm-dust to simulate a failing block device
- # The dev_img file is 1MB: 2048 blocks of 512 bytes each
- self.assertRunOk(f"dmsetup create {dm_dev_name} --table '0 2048 dust {lo_dev} 0 512'")
- # Add few bad blocks and enable I/O error emulation
- for badblock in [30, 40, 50, 60]:
- self.assertRunOk(f"dmsetup message {dm_dev_name} 0 addbadblock {badblock}")
- self.assertRunOk(f"dmsetup message {dm_dev_name} 0 enable")
- # Show device mapper status, to make debugging easier
- self.assertRunOk(f"dmsetup status {dm_dev_name}")
- # A normal 'dd' is expected to fail with I/O error
- cmd = f"dd if={dm_dev} of=/dev/null bs=512"
- _, exit_code = self.emulator.run(cmd)
- self.assertNotEqual(exit_code, 0)
- # Where a normal 'dd' fails, 'ddrescue' is expected to succeed
- self.assertRunOk(f"ddrescue {dm_dev} {ddrescue_img}")
- # ddrescue does not normally write any output data when there
- # is I/O error on the input. The intent is to preserve any
- # data that could have been read in a previous pass. There is
- # one exception, when the output is a non-existing regular
- # file, ddrescue will initialize it with zeroes the first
- # time. Since the original image file was also including
- # zeroes, the recovered image is expected to be the same as
- # the original one. See ddrescue manual:
- # https://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html#Introduction
- # "Ddrescue does not write zeros to the output when it finds
- # bad sectors in the input, and does not truncate the output
- # file if not asked to."
- # https://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html#Algorithm
- # "If the output file is a regular file created by ddrescue,
- # the areas marked as bad-sector will contain zeros."
- self.assertRunOk(f"cmp {dev_img} {ddrescue_img}")
|