blob: ecfe1f279f3f8a454590a3fcc1ef4e1a9558f12a [file] [log] [blame]
# Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import os
import re
import traceback
from autotest_lib.client.common_lib import error
from autotest_lib.server import test
from autotest_lib.client.bin import utils
class platform_CorruptRootfs(test.test):
"""Tests how the system recovers when the root file system is corrupted
1. Copies kernel A and rootfs A to kernel B and rootfs B.
2. Sets the modes on the partitions.
3. Corrupts rootfs A by writing some random data to a the first few sectors.
4. If enabled, corrupts the bootcache area.
5. Reboots the system.
6. Runs the test again in the reverse direction, leaving kernel A and
rootfs A in the same state.
"""
version = 1
def _get_bootcache_offset(self):
"""Gets the offset of the bootcache from the command line.
@return
if bootcache is found, returns offset as a string
otherwise returns '0'
"""
# Get the linux cmd line
result = self.client.run('cat /proc/cmdline')
m = re.search('dm="(.*)"', result.stdout)
dm = m.group(1)
i = dm.find('bootcache')
if i > 0:
s = dm[i:].split()
return s[2] # 2nd field after bootcache has sector offset
return '0'
def _get_partition_layout(self):
"""Get the partition layout
@return
dev - name of the device hosting the partition
kernelA - partition used to boot kernel
rootfsA - partition of current root file system
kernelB - backup copy of kernel
rootfsB - backup copy of root file system
"""
# What is our root partition?
# TODO(crbug.com/226082)
result = self.client.run('rootdev -s')
logging.info('Root partition %s', result.stdout)
rootdev = result.stdout.strip()
if os.path.basename(rootdev).startswith('mmc'):
dev = rootdev[:-2]
else:
dev = rootdev[:-1]
kernelA = utils.get_kernel_partition(rootdev)
rootfsA = rootdev
kernelB = utils.get_free_kernel_partition(rootdev)
rootfsB = utils.get_free_root_partition(rootdev)
return dev, kernelA, rootfsA, kernelB, rootfsB
def _corrupt_rootfs(self, host):
"""Corrupt the root file system
"""
self.client = host
self.client_test = 'platform_CorruptRootfs'
dev, kernelA, rootfsA, kernelB, rootfsB = self._get_partition_layout()
bootcache_offset = self._get_bootcache_offset()
# Copy kernel and rootfs paritions from A to B
logging.info('CorruptRootfs: copy partitions A to B')
self.client.run('dd if=%s of=%s bs=64K' % (kernelA, kernelB))
self.client.run('dd if=%s of=%s bs=64K' % (rootfsA, rootfsB))
# Set attribrutes on kernel A and B
logging.info('CorruptRootfs: set attributes on kernal A and B')
self.client.run('cgpt add -i 2 -T 5 -P 9 -S 1 %s' % dev)
self.client.run('cgpt add -i 4 -T 5 -P 9 -S 1 %s' % dev)
# Corrupt rootfs A and bootcache
logging.info('CorruptRootfs: corrupt rootfs A ' + rootfsA)
self.client.run('dd if=/dev/urandom of=%s count=16' % rootfsA)
if bootcache_offset != '0':
self.client.run('dd if=/dev/zero of=%s seek=%s count=4096' %
(rootfsA, bootcache_offset))
logging.info('CorruptRootfs: reboot ' + self.client.hostname)
try:
self.client.reboot()
except error.AutoservRebootError as e:
raise error.TestFail('%s.\nTest failed with error %s' % (
traceback.format_exc(), str(e)))
# Find what partition we are now running on
result = self.client.run('rootdev -s')
logging.info('Root partition %s', result.stdout)
def run_once(self, host=None):
"""
run_once actually runs the test twice. The second run "undoes"
what was done in the first run.
@param host - the host machine running the test
"""
self._corrupt_rootfs(host)
self._corrupt_rootfs(host)