Home | History | Annotate | Download | only in utils
      1 # Copyright (c) 2014 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 
      5 """A module containing rootfs handler class."""
      6 
      7 import os
      8 import re
      9 
     10 TMP_FILE_NAME = 'kernel_dump'
     11 
     12 _KERNEL_MAP = {'A': '2', 'B': '4'}
     13 _ROOTFS_MAP = {'A': '3', 'B': '5'}
     14 _DM_DEVICE = 'verifyroot'
     15 _DM_DEV_PATH = os.path.join('/dev/mapper', _DM_DEVICE)
     16 
     17 
     18 class RootfsHandler(object):
     19     """An object to provide ChromeOS root FS related actions.
     20 
     21     It provides functions to verify the integrity of the root FS.
     22     """
     23 
     24     def __init__(self):
     25         self.os_if = None
     26         self.root_dev = None
     27         self.kernel_dump_file = None
     28 
     29     def verify_rootfs(self, section):
     30         """Verifies the integrity of the root FS.
     31 
     32         @param section: The rootfs to verify. May be A or B.
     33         """
     34         kernel_path = self.os_if.join_part(self.root_dev,
     35                 _KERNEL_MAP[section.upper()])
     36         rootfs_path = self.os_if.join_part(self.root_dev,
     37                 _ROOTFS_MAP[section.upper()])
     38         # vbutil_kernel won't operate on a device, only a file.
     39         self.os_if.run_shell_command(
     40                 'dd if=%s of=%s' % (kernel_path, self.kernel_dump_file))
     41         vbutil_kernel = self.os_if.run_shell_command_get_output(
     42                 'vbutil_kernel --verify %s --verbose' % self.kernel_dump_file)
     43         DM_REGEXP = re.compile(r'dm="(?:1 )?vroot none ro(?: 1)?,(0 (\d+) .+)"')
     44         match = DM_REGEXP.search('\n'.join(vbutil_kernel))
     45         if not match:
     46             return False
     47 
     48         table = match.group(1)
     49         partition_size = int(match.group(2)) * 512
     50 
     51         if table.find('PARTUUID=%U/PARTNROFF=1') < 0:
     52             return False
     53         table = table.replace('PARTUUID=%U/PARTNROFF=1', rootfs_path)
     54         # Cause I/O error on invalid bytes
     55         table += ' error_behavior=eio'
     56 
     57         self._remove_mapper()
     58         assert not self.os_if.path_exists(_DM_DEV_PATH)
     59         self.os_if.run_shell_command(
     60                 "dmsetup create -r %s --table '%s'" % (_DM_DEVICE, table))
     61         assert self.os_if.path_exists(_DM_DEV_PATH)
     62         try:
     63             count = self.os_if.get_file_size(_DM_DEV_PATH)
     64             return count == partition_size
     65         except:
     66             return False
     67         finally:
     68             self._remove_mapper()
     69 
     70     def _remove_mapper(self):
     71         """Removes the dm device mapper used by this class."""
     72         if self.os_if.path_exists(_DM_DEV_PATH):
     73             self.os_if.run_shell_command_get_output(
     74                     'dmsetup remove %s' % _DM_DEVICE)
     75 
     76     def init(self, os_if):
     77         """Initialize the rootfs handler object.
     78 
     79         @param os_if: OS interface object reference.
     80         """
     81         self.os_if = os_if
     82         self.root_dev = os_if.get_root_dev()
     83         self.kernel_dump_file = os_if.state_dir_file(TMP_FILE_NAME)
     84