1# Copyright (c) 2010 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 to provide interface to gpt information. 6 7gpt stands for GUID partition table, it is a data structure describing 8partitions present of a storage device. cgpt is a utility which allows to read 9and modify gpt. This module parses cgpt output to create a dictionary 10including information about all defined partitions including their properties. 11It also allows to modify partition properties as required. 12""" 13 14class CgptError(Exception): 15 pass 16 17class CgptHandler(object): 18 """Object representing one or more gpts present in the system. 19 20 Attributes: 21 os_if: an instance of OSInterface, initialized by the caller. 22 devices: a dictionary keyed by the storage device names (as in 23 /dev/sda), the contents are dictionaries of cgpt information, 24 where keys are partiton names, and contents are in turn 25 dictionaries of partition properties, something like the below 26 (compressed for brevity): 27 {'/dev/sda': { 28 'OEM': {'partition': 8, 'Type': 'Linux data', 'UUID': 'xxx'}, 29 'ROOT-A': {'partition': 3, 'Type': 'ChromeOS rootfs', 'UUID': 'xyz'}, 30 'ROOT-C': {'partition': 7, 'Type': 'ChromeOS rootfs', 'UUID': 'xzz'}, 31 'ROOT-B': {'partition': 5, 'Type': 'ChromeOS rootfs', 'UUID': 'aaa'}, 32 ... 33 } 34 } 35 36 """ 37 38 # This dictionary maps gpt attributes the user can modify into the cgpt 39 # utility command line options. 40 ATTR_TO_COMMAND = { 41 'priority' : 'P', 42 'tries' : 'T', 43 'successful' : 'S' 44 } 45 46 def __init__(self, os_if): 47 self.os_if = os_if 48 self.devices = {} 49 50 def read_device_info(self, dev_name): 51 """Get device information from cgpt and parse it into a dictionary. 52 53 Inputs: 54 dev_name: a string the Linux storage device name, (i.e. '/dev/sda') 55 """ 56 57 device_dump = self.os_if.run_shell_command_get_output( 58 'cgpt show %s' % dev_name) 59 label = None 60 label_data = {} 61 device_data = {} 62 for line in [x.strip() for x in device_dump]: 63 if 'Label:' in line: 64 if label and label not in device_data: 65 device_data[label] = label_data 66 _, _, partition, _, label = line.split() 67 label = line.split('Label:')[1].strip('" ') 68 label_data = {'partition': int(partition)} 69 continue 70 if ':' in line: 71 name, value = line.strip().split(':') 72 if name != 'Attr': 73 label_data[name] = value.strip() 74 continue 75 # Attributes are split around '=', each attribute becomes a 76 # separate partition property. 77 attrs = value.strip().split() 78 for attr in attrs: 79 name, value = attr.split('=') 80 label_data[name] = int(value) 81 if label_data: 82 device_data[label] = label_data 83 84 self.devices[dev_name] = device_data 85 86 def get_partition(self, device, partition_name): 87 """Retrieve a dictionary representing a partition on a device. 88 89 Inputs: 90 device: a string, the Linux device name 91 partition_name: a string, the partition name as reported by cgpt. 92 93 Raises: 94 CgptError in case the device or partiton on that device are not 95 known. 96 """ 97 98 try: 99 result = self.devices[device][partition_name] 100 except KeyError: 101 raise CgptError('could not retrieve partiton %s of device %s' % ( 102 partition_name, device)) 103 return result 104 105 def set_partition(self, device, partition_name, partition_value): 106 """Set partition properties. 107 108 Inputs: 109 device: a string, the Linux device name 110 partition_name: a string, the partition name as reported by cgpt. 111 partiton_value: a dictionary, where keys are strings, names of the 112 properties which need to be modified, and values are the 113 values to set the properties to. The only properties which 114 can be modified are those which are keys of ATTR_TO_COMMAND 115 defined above. 116 Raises: 117 CgptError in case a property name is not known or not supposed to 118 be modified. 119 """ 120 121 current = self.get_partition(device, partition_name) 122 options = [] 123 for prop, value in partition_value.iteritems(): 124 try: 125 if value == current[prop]: 126 continue 127 options.append('-%s %d' % ( 128 self.ATTR_TO_COMMAND[prop], value)) 129 except KeyError: 130 raise CgptError("unknown or immutable property '%s'" % prop) 131 132 if not options: 133 return 134 135 command = 'cgpt add -i %d %s %s' % ( 136 current['partition'], ' '.join(options), device) 137 self.os_if.run_shell_command(command) 138