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_SyncCrash(test.test):
11    """Tests syncing to the file system before a crash.
12
13    This test works in conjunction with testsync (found in
14    punybench) to test various forms of sync. Testsync runs
15    on the client to create a file, fill it with data, sync
16    that data and crash the system.  The server side (this
17    code), waits for the client to restart and then invokes
18    testsync to verify the data did sync.
19
20    Testsync has different phases for testing sync
21    writer - syncs a file using fsync
22    mapper - syncs a memory mapped file using msync
23    verifier - verifies that content of the file
24
25    The tests are run on both the stateful partition
26    and ecryptfs mounted on a directory in the stateful
27    partition.
28    """
29    version = 1
30    _STATEFUL_DIR = '/usr/local/CrashDir/'
31    _ECRYPT_DIR = '/usr/local/ecryptfs_tst/'
32    _ECRYPT_MOUNT_POINT = '/usr/local/ecryptfs_mnt/'
33    _ECRYPT_TEST_DIR = '%s/CrashDir/' % _ECRYPT_MOUNT_POINT
34    _FILE = 'xyzzy'
35    _Testsync = '/usr/local/opt/punybench/bin/testsync'
36
37
38    def _run(self, cmd):
39        """Run the given command and log results
40
41        @param cmd: command to be run
42        """
43        result = self.client.run(cmd)
44        if result.exit_status != 0:
45            logging.error('%s: %s', cmd, result.stdout)
46        else:
47            logging.info('%s', cmd)
48
49
50    def _testsync_crash(self, args):
51        """Run testsync on the client
52
53        Since testsync forces a crash of the system, the code
54        waits for the reboot.
55
56        @param args: arguments to pass testsync
57        """
58        logging.info('Crash: %s', self.client.hostname)
59        try:
60            cmd = '%s %s' % (self._Testsync, args)
61            logging.info('Crash: %s', cmd)
62            self.client.reboot(reboot_cmd=cmd)
63        except error.AutoservRebootError as e:
64            raise error.TestFail('%s.\nTest failed with error %s' % (
65                    traceback.format_exc(), str(e)))
66
67
68    def _testsync_verify(self, args):
69        """Verify the results from previous testsync
70
71        @param args: arguments to pass to testsync
72        """
73        cmd = '%s %s -p verifier' % (self._Testsync, args)
74        result = self.client.run(cmd)
75        if result.exit_status != 0:
76            logging.error('%s: %s', cmd, result.stdout)
77
78
79    def _ecrypt_setup(self):
80        """Setup an eCrypt File System and mount it
81        """
82        edir = self._ECRYPT_DIR
83        mnt = self._ECRYPT_MOUNT_POINT
84        options = ('-o'
85                   ' key=passphrase:passphrase_passwd=secret'
86                   ',ecryptfs_cipher=aes'
87                   ',ecryptfs_key_bytes=32'
88                   ',no_sig_cache'
89                   ',ecryptfs_passthrough=no'
90                   ',ecryptfs_enable_filename_crypto=no')
91        self._run('mkdir -p %s %s' % (edir, mnt))
92        self._run('mount -t ecryptfs %s %s %s' %
93                           (options, edir, mnt))
94
95
96    def _ecrypt_teardown(self):
97        """Teardown the eCrypt File System
98        """
99        edir = self._ECRYPT_DIR
100        mnt = self._ECRYPT_MOUNT_POINT
101        self._run('umount %s' % mnt)
102        self._run('rm -R %s %s' % (edir, mnt))
103
104
105    def _sync_stateful(self, dir, phase):
106        """Crash the stateful file system while changing it
107
108        @param dir - directory where test files are created
109        @param phase - which phase of testsync to invoke
110        """
111        size = 0x1000
112        file = dir + self._FILE
113        self._run('mkdir -p %s' % dir)
114        self._testsync_crash('-f %s -z %s -p %s' % (file, size, phase))
115        self._testsync_verify('-f %s -z %s' % (file, size))
116        self._run('rm -r %s' % dir)
117
118
119    def _sync_ecryptfs(self, dir, phase):
120        """Crash an ecryptfs file system right after a sync
121
122        @param edir - directory used for the encrypted file system
123        @param mnt - mount point for the encrypted file system
124        @param dir - directory where test files are created
125        @param phase - which phase of testsync to invoke
126        """
127        size = 0x1000
128        file = dir + self._FILE
129        self._ecrypt_setup()
130        self._run('mkdir -p %s' % dir)
131        self._testsync_crash('-f %s -z %s -p %s' % (file, size, phase))
132        self._ecrypt_setup()
133        self._testsync_verify('-f %s -z %s' % (file, size))
134        self._run('rm -r %s' % dir)
135        self._ecrypt_teardown()
136
137
138    def run_once(self, host=None):
139        """run_once runs the test.
140
141        1. Runs a crash test on stateful partition
142        2. Create an ecryptfs volume and run the same
143           crash test
144
145        @param host - the host machine running the test
146        """
147        self.client = host
148
149        self._sync_stateful(self._STATEFUL_DIR, 'writer')
150        self._sync_stateful(self._STATEFUL_DIR, 'mapper')
151
152        self._sync_ecryptfs(self._ECRYPT_TEST_DIR, 'writer')
153        self._sync_ecryptfs(self._ECRYPT_TEST_DIR, 'mapper')
154