1# Copyright 2017 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import glob 6import logging 7import os 8import tempfile 9 10from autotest_lib.client.bin import test 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib import utils 13 14PARTITION_TEST_PATH = 'platform_SecureEraseFile_test_file' 15TEST_PATH = '/mnt/stateful_partition/' + PARTITION_TEST_PATH 16BINARY = '/usr/bin/secure_erase_file' 17DEVNAME_PREFIX = 'DEVNAME=' 18 19class platform_SecureEraseFile(test.test): 20 """Validate secure_erase_file tool behavior. 21 22 We can't verify from this test that data has been destroyed from the 23 underlying physical device, but we can confirm that it's not reachable from 24 userspace. 25 """ 26 version = 1 27 28 def __write_test_file(self, path, blocksize, count): 29 cmd = '/bin/dd if=/dev/urandom of=%s bs=%s count=%d' % ( 30 path, blocksize, count) 31 utils.run(cmd) 32 if not os.path.exists(path): 33 raise error.TestError('Failed to generate test file') 34 35 36 def __get_partition(self, path): 37 info = os.lstat(path) 38 major = os.major(info.st_dev) 39 minor = os.minor(info.st_dev) 40 uevent_path = '/sys/dev/block/%d:%d/uevent' % (major, minor) 41 with open(uevent_path, 'r') as uevent_file: 42 for l in uevent_file.readlines(): 43 if l.startswith(DEVNAME_PREFIX): 44 return '/dev/' + l[len(DEVNAME_PREFIX):].strip() 45 raise error.TestError('Unable to find partition for path: ' + path) 46 47 48 def __get_extents(self, path, partition): 49 extents = [] 50 cmd = 'debugfs -R "extents %s" %s' % (path, partition) 51 result = utils.run(cmd) 52 for line in result.stdout.splitlines(): 53 # Discard header line. 54 if line.startswith('Level'): 55 continue 56 fields = line.split() 57 58 # Ignore non-leaf extents 59 if fields[0].strip('/') != fields[1]: 60 continue 61 extents.append({'offset': fields[7], 'length': fields[10]}) 62 63 return extents 64 65 66 def __verify_cleared(self, partition, extents): 67 out_path = tempfile.mktemp() 68 for e in extents: 69 cmd = 'dd if=%s bs=4K skip=%s count=%s of=%s' % ( 70 partition, e['offset'], e['length'], out_path) 71 utils.run(cmd) 72 with open(out_path, 'r') as out_file: 73 d = out_file.read() 74 for i, byte in enumerate(d): 75 if ord(byte) != 0x00 and ord(byte) != 0xFF: 76 logging.info('extent[%d] = %s', i, hex(ord(byte))) 77 raise error.TestError('Bad byte found') 78 79 80 def __test_and_verify_cleared(self, blocksize, count): 81 self.__write_test_file(TEST_PATH, blocksize, count) 82 utils.run('sync') 83 84 logging.info('original file contents: ') 85 res = utils.run('xxd %s' % TEST_PATH) 86 logging.info(res.stdout) 87 88 partition = self.__get_partition(TEST_PATH) 89 extents = self.__get_extents(PARTITION_TEST_PATH, partition) 90 if len(extents) == 0: 91 raise error.TestError('No extents found for ' + TEST_PATH) 92 93 utils.run('%s %s' % (BINARY, TEST_PATH)) 94 95 # secure_erase_file confirms that the file has been erased and that its 96 # contents are not accessible. If that is not the case, it will return 97 # with a non-zero exit code. 98 if os.path.exists(TEST_PATH): 99 raise error.TestError('Secure Erase failed to unlink file.') 100 101 self.__verify_cleared(partition, extents) 102 103 104 def run_once(self): 105 # Secure erase is only supported on eMMC today; pass if 106 # no device is present. 107 if len(glob.glob('/dev/mmcblk*')) == 0: 108 raise error.TestNAError('Skipping test; no eMMC device found.') 109 110 self.__test_and_verify_cleared('64K', 2) 111 self.__test_and_verify_cleared('1M', 16) 112 113 def after_run_once(self): 114 if os.path.exists(TEST_PATH): 115 os.unlink(TEST_PATH) 116 117