• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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