test_ddrescue.py 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import os
  2. import infra.basetest
  3. class TestDdrescue(infra.basetest.BRTest):
  4. # A specific configuration is needed for testing ddrescue:
  5. # - A kernel config fragment enables loop blk dev and device
  6. # mapper dm-dust, which are used to simulate a failing storage
  7. # block device.
  8. # - dmraid user space package is needed to configure dm-dust
  9. config = \
  10. """
  11. BR2_aarch64=y
  12. BR2_TOOLCHAIN_EXTERNAL=y
  13. BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
  14. BR2_LINUX_KERNEL=y
  15. BR2_LINUX_KERNEL_CUSTOM_VERSION=y
  16. BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.15"
  17. BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
  18. BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
  19. BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
  20. BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
  21. BR2_PACKAGE_DDRESCUE=y
  22. BR2_PACKAGE_DMRAID=y
  23. BR2_TARGET_ROOTFS_CPIO=y
  24. BR2_TARGET_ROOTFS_CPIO_GZIP=y
  25. # BR2_TARGET_ROOTFS_TAR is not set
  26. """.format(
  27. infra.filepath("tests/package/test_ddrescue/linux-ddrescue.fragment")
  28. )
  29. def test_run(self):
  30. img = os.path.join(self.builddir, "images", "rootfs.cpio.gz")
  31. kern = os.path.join(self.builddir, "images", "Image")
  32. self.emulator.boot(arch="aarch64",
  33. kernel=kern,
  34. kernel_cmdline=["console=ttyAMA0"],
  35. options=["-M", "virt", "-cpu", "cortex-a57", "-m", "256M", "-initrd", img])
  36. self.emulator.login()
  37. # Test variables:
  38. dev_img = "/tmp/dev.img"
  39. lo_dev = "/dev/loop0"
  40. dm_dev_name = "dust0"
  41. dm_dev = f"/dev/mapper/{dm_dev_name}"
  42. ddrescue_img = "/tmp/ddrescue.img"
  43. # Test the program can execute
  44. self.assertRunOk("ddrescue --version")
  45. # Create a 1MB file of zeroes for initial loopback block device
  46. self.assertRunOk(f"dd if=/dev/zero of={dev_img} bs=1M count=1")
  47. # Setup lookback block device
  48. self.assertRunOk(f"losetup {lo_dev} {dev_img}")
  49. # Create and setup dm-dust to simulate a failing block device
  50. # The dev_img file is 1MB: 2048 blocks of 512 bytes each
  51. self.assertRunOk(f"dmsetup create {dm_dev_name} --table '0 2048 dust {lo_dev} 0 512'")
  52. # Add few bad blocks and enable I/O error emulation
  53. for badblock in [30, 40, 50, 60]:
  54. self.assertRunOk(f"dmsetup message {dm_dev_name} 0 addbadblock {badblock}")
  55. self.assertRunOk(f"dmsetup message {dm_dev_name} 0 enable")
  56. # Show device mapper status, to make debugging easier
  57. self.assertRunOk(f"dmsetup status {dm_dev_name}")
  58. # A normal 'dd' is expected to fail with I/O error
  59. cmd = f"dd if={dm_dev} of=/dev/null bs=512"
  60. _, exit_code = self.emulator.run(cmd)
  61. self.assertNotEqual(exit_code, 0)
  62. # Where a normal 'dd' fails, 'ddrescue' is expected to succeed
  63. self.assertRunOk(f"ddrescue {dm_dev} {ddrescue_img}")
  64. # ddrescue does not normally write any output data when there
  65. # is I/O error on the input. The intent is to preserve any
  66. # data that could have been read in a previous pass. There is
  67. # one exception, when the output is a non-existing regular
  68. # file, ddrescue will initialize it with zeroes the first
  69. # time. Since the original image file was also including
  70. # zeroes, the recovered image is expected to be the same as
  71. # the original one. See ddrescue manual:
  72. # https://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html#Introduction
  73. # "Ddrescue does not write zeros to the output when it finds
  74. # bad sectors in the input, and does not truncate the output
  75. # file if not asked to."
  76. # https://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html#Algorithm
  77. # "If the output file is a regular file created by ddrescue,
  78. # the areas marked as bad-sector will contain zeros."
  79. self.assertRunOk(f"cmp {dev_img} {ddrescue_img}")