1# Copyright (c) 2013 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 logging
6import traceback
7from autotest_lib.client.common_lib import error
8from autotest_lib.server import test
9
10class platform_CrashStateful(test.test):
11    """Tests the crash recovery of the stateful file system
12
13    1. Create a specific file 'charlie'
14    2. Sync
15    3. Crash system
16    4. Wait for reboot
17    5. Check if 'charlie' is there and complete
18    6. Clean up
19
20    Do the samething with an ecryptfs volume.
21    """
22    version = 1
23    _STATEFUL_DIR = '/usr/local/CrashDir'
24    _ECRYPT_DIR = '/usr/local/ecryptfs_tst'
25    _ECRYPT_MOUNT_POINT = '/usr/local/ecryptfs_mnt'
26    _ECRYPT_TEST_DIR = '%s/CrashDir' % _ECRYPT_MOUNT_POINT
27
28
29    def _run(self, cmd):
30        """Run the give command and log results
31
32        @param cmd: command to be run
33        """
34        result = self.client.run(cmd)
35        if result.exit_status != 0:
36            logging.error('%s: %s', cmd, result.stdout)
37
38
39    def _ecrypt_mount(self, edir, mnt):
40        """Mount the eCrypt File System
41
42        @param ddir: directory where encrypted file system is stored
43        @param mnt: mount point for encrypted file system
44        """
45        options = ('-o'
46                   ' key=passphrase:passphrase_passwd=secret'
47                   ',ecryptfs_cipher=aes'
48                   ',ecryptfs_key_bytes=32'
49                   ',no_sig_cache'
50                   ',ecryptfs_passthrough=no'
51                   ',ecryptfs_enable_filename_crypto=no')
52        self._run('mkdir -p %s %s' % (edir, mnt))
53        self._run('mount -t ecryptfs %s %s %s' %
54                           (options, edir, mnt))
55
56
57    def _ecrypt_unmount(self, edir, mnt):
58        """Unmount the eCrypt File System and remove it and its mount point
59
60        @param dir: directory where encrypted file system is stored
61        @param mnt: mount point for encrypted file system
62        """
63        self._run('umount %s' % edir)
64        self._run('rm -R %s' % edir)
65        self._run('rm -R %s' % mnt)
66
67
68    def _crash(self):
69        """crash the client without giving anything a chance to clean up
70
71        We use the kernel crash testing interface to immediately reboot the
72        system. No chance for any flushing of I/O or cleaning up.
73        """
74        logging.info('CrashStateful: force panic %s', self.client.hostname)
75        interface = "/sys/kernel/debug/provoke-crash/DIRECT"
76        cmd = 'echo PANIC > %s' % interface
77        if not self.client.run('ls %s' % interface,
78                               ignore_status=True).exit_status == 0:
79            interface = "/proc/breakme"
80            cmd = 'echo panic > %s' % interface
81        try:
82            """The following is necessary to avoid command execution errors
83            1) If ssh on the DUT doesn't terminate cleanly, it will exit with
84               status 255 causing an exception
85            2) ssh won't terminate if a background process holds open stdin,
86               stdout, or stderr
87            3) without a sleep delay, the reboot may close the connection with
88               an error
89            """
90            wrapped_cmd = 'sleep 1; %s'
91            self.client.reboot(reboot_cmd=wrapped_cmd % cmd)
92        except error.AutoservRebootError as e:
93            raise error.TestFail('%s.\nTest failed with error %s' % (
94                    traceback.format_exc(), str(e)))
95
96
97    def _create_file_and_crash(self, dir):
98        """Sets up first part of test, then crash
99
100        @param dir - directory where test files are created
101        """
102        self._run('mkdir -p %s' % dir)
103        self._run('echo charlie smith >%s/charlie' % dir)
104        self._run('sync')
105        self._crash()
106
107
108    def _verify_and_cleanup(self, dir):
109        """Verify results and clean up
110
111        @param dir - directory where test files were created
112        """
113        result = self.client.run('cat %s/charlie' % dir)
114        hi = result.stdout.strip()
115        if hi != 'charlie smith':
116            raise error.TestFail('Test failed, Sync mechanism failed')
117        self._run('rm -fr %s' % dir)
118
119
120    def _crash_stateful(self, dir):
121        """Crash the stateful file system while changing it
122
123        @param dir - directory where test files are created
124        """
125        self._create_file_and_crash(dir)
126        self._verify_and_cleanup(dir)
127
128
129    def _crash_ecrptfs(self, edir, mnt, dir):
130        """Crash the stateful file system while changing it
131
132        @param edir - directory used for the encrypted file system
133        @param mnt - mount point for the encrypted file system
134        @param dir - directory where test files are created
135        """
136        self._ecrypt_mount(edir, mnt)
137        self._create_file_and_crash(dir)
138        self._ecrypt_mount(edir, mnt)
139        self._verify_and_cleanup(dir)
140        self._ecrypt_unmount(edir, mnt)
141
142
143    def run_once(self, host=None):
144        """run_once runs the test.
145
146        1. Runs a crash test on stateful partition
147        2. Create an ecryptfs volume and run the same
148           crash test
149
150        @param host - the host machine running the test
151        """
152        self.client = host
153
154        self._crash_stateful(self._STATEFUL_DIR)
155
156        self._crash_ecrptfs(self._ECRYPT_DIR, self._ECRYPT_MOUNT_POINT,
157            self._ECRYPT_TEST_DIR)
158