Revision c0240ac1

b/image_creator/gpt.py
40 40

  
41 41

  
42 42
class MBR(object):
43
    """Represents a Master Boot Record."""
43 44
    class Partition(object):
44 45
        format = "<B3sB3sLL"
45 46

  
46 47
        def __init__(self, raw_part):
47

  
48
            (   self.status,
48
            (
49
                self.status,
49 50
                self.start,
50 51
                self.type,
51 52
                self.end,
......
63 64
                self.sector_count
64 65
            )
65 66

  
66
        def show(self):
67
        @staticmethod
68
        def size():
69
            """Returns the size of an MBR partition entry"""
70
            return struct.calcsize(MBR.Partition.format)
71

  
72
        def __str__(self):
67 73
            start = self.unpack_chs(self.start)
68 74
            end = self.unpack_chs(self.end)
69
            print "%d %s %d %s %d %d" % (self.status, start, self.type, end,
75
            return "%d %s %d %s %d %d" % (self.status, start, self.type, end,
70 76
                self.first_sector, self.sector_count)
71 77

  
72 78
        def unpack_chs(self, chs):
79
            """Unpacks a CHS address string to a tuple."""
73 80

  
74 81
            assert len(chs) == 3
75 82

  
......
81 88
            return (cylinder, head, sector)
82 89

  
83 90
        def pack_chs(self, cylinder, head, sector):
91
            """Packs a CHS tuple to an address string."""
84 92

  
85 93
            assert 1 <= sector <= 63
86 94
            assert 0 <= cylinder <= 1023
......
117 125
        for i in range(4):
118 126
            self.part[i] = self.Partition(raw_part[i])
119 127

  
128
    @staticmethod
129
    def size():
130
        """Returns the size of a Master Boot Record."""
131
        return struct.calcsize(MBR.format)
132

  
120 133
    def pack(self):
134
        """Packs an MBR to a binary string."""
121 135
        return struct.pack(self.format,
122 136
            self.code_area,
123 137
            self.part[0].pack(),
......
127 141
            self.signature
128 142
        )
129 143

  
130
    def show(self):
144
    def __str__(self):
145
        ret = ""
131 146
        for i in range(4):
132
            print "Part %d: " % i,
133
            self.part[i].show()
147
            ret += "Partition %d: %s\n" % (i, self.part[i])
148
        ret += "Signature: %s %s\n" % (
149
                    hex(ord(self.signature[0])), hex(ord(self.signature[1])))
150
        return ret
134 151

  
135 152

  
136 153
class GPTPartitionTable(object):
154
    """Represents a GUID Partition Table."""
137 155
    class GPTHeader(object):
156
        """Represents a GPT Header of a GUID Partition Table."""
138 157
        format = "<8s4sII4xQQQQ16sQIII"
139 158
        """
140 159
        Offset	Length 	        Contents
......
159 178
        def __init__(self, block):
160 179
            self.signature, \
161 180
            self.revision, \
162
            self.size, \
181
            self.hdr_size, \
163 182
            self.header_crc32, \
164 183
            self.current_lba, \
165 184
            self.backup_lba, \
......
172 191
            self.part_crc32 = struct.unpack(self.format, block)
173 192

  
174 193
        def pack(self):
194
            """Packs a GPT Header to a binary string."""
175 195
            return struct.pack(self.format,
176 196
                self.signature, \
177 197
                self.revision, \
178
                self.size, \
198
                self.hdr_size, \
179 199
                self.header_crc32, \
180 200
                self.current_lba, \
181 201
                self.backup_lba, \
......
188 208
                self.part_crc32
189 209
            )
190 210

  
191
        def show(self):
192
            print "Signature: %s" % self.signature
193
            print "Revision: %r" % self.revision
194
            print "Header Size: %d" % self.size
195
            print "CRC32: %d" % self.header_crc32
196
            print "Current LBA: %d" % self.current_lba
197
            print "Backup LBA: %d" % self.backup_lba
198
            print "First Usable LBA: %d" % self.first_usable_lba
199
            print "Last Usable LBA: %d" % self.last_usable_lba
200
            print "Disk GUID: %s" % uuid.UUID(bytes=self.uuid)
201
            print "Partition entries starting LBA: %d" % self.part_entry_start
202
            print "Number of Partition entries: %d" % self.part_count
203
            print "Size of a partition entry: %d" % self.part_entry_size
204
            print "CRC32 of partition array: %s" % self.part_crc32
211
        @staticmethod
212
        def size():
213
            """Returns the size of a GPT Header."""
214
            return struct.calcsize(GPTPartitionTable.GPTHeader.format)
215

  
216
        def __str__(self):
217
            return \
218
            "Signature: %s\n" % self.signature + \
219
            "Revision: %r\n" % self.revision + \
220
            "Header Size: %d\n" % self.hdr_size + \
221
            "CRC32: %d\n" % self.header_crc32 + \
222
            "Current LBA: %d\n" % self.current_lba + \
223
            "Backup LBA: %d\n" % self.backup_lba + \
224
            "First Usable LBA: %d\n" % self.first_usable_lba + \
225
            "Last Usable LBA: %d\n" % self.last_usable_lba + \
226
            "Disk GUID: %s\n" % uuid.UUID(bytes=self.uuid) + \
227
            "Partition entries starting LBA: %d\n" % self.part_entry_start + \
228
            "Number of Partition entries: %d\n" % self.part_count + \
229
            "Size of a partition entry: %d\n" % self.part_entry_size + \
230
            "CRC32 of partition array: %s\n" % self.part_crc32
205 231

  
206 232
    def __init__(self, disk):
207 233
        self.disk = disk
......
209 235
            #MBR (Logical block address 0)
210 236
            lba0 = d.read(BLOCKSIZE)
211 237
            self.mbr = MBR(lba0)
238

  
212 239
            # Primary GPT Header (LBA 1)
213
            lba1 = d.read(BLOCKSIZE)
214
            self.primary = self.GPTHeader(lba1[:92])
240
            raw_header = d.read(self.GPTHeader.size())
241
            self.primary = self.GPTHeader(raw_header)
242

  
215 243
            # Partition entries (LBA 2...34)
216 244
            d.seek(self.primary.part_entry_start * BLOCKSIZE)
217 245
            entries_size = self.primary.part_count * \
218 246
                                                self.primary.part_entry_size
219 247
            self.part_entries = d.read(entries_size)
248

  
220 249
            # Secondary GPT Header (LBA -1)
221 250
            d.seek(self.primary.backup_lba * BLOCKSIZE)
222
            lba_1 = d.read(BLOCKSIZE)
223
            self.secondary = self.GPTHeader(lba_1[:92])
251
            raw_header = d.read(self.GPTHeader.size())
252
            self.secondary = self.GPTHeader(raw_header)
224 253

  
225 254
    def size(self):
226
        return (self.primary.backup_lba + 1) * BLOCKSIZE 
255
        """Returns the payload size of GPT partitioned device."""
256
        return (self.primary.backup_lba + 1) * BLOCKSIZE
227 257

  
228 258
    def shrink(self, size):
229

  
259
        """Move the secondary GPT Header entries to the address specified by
260
        size parameter.
261
        """
230 262
        if size == self.size():
231 263
            return size
232 264

  
......
238 270
        lba_count = new_size // BLOCKSIZE
239 271

  
240 272
        # Correct MBR
241
        #TODO: Check for hybrid partition tables
273
        #TODO: Check if the partition tables is hybrid
242 274
        self.mbr.part[0].sector_count = (new_size // BLOCKSIZE) - 1
243 275

  
244
        # Correct Primary header
276
        # Fix Primary header
245 277
        self.primary.header_crc32 = 0
246 278
        self.primary.backup_lba = lba_count - 1  # LBA-1
247 279
        self.primary.last_usable_lba = lba_count - 34  # LBA-34
248 280
        self.primary.header_crc32 = \
249 281
                            binascii.crc32(self.primary.pack()) & 0xffffffff
250 282

  
251
        # Correct Secondary header entries
283
        # Fix Secondary header
252 284
        self.secondary.header_crc32 = 0
253 285
        self.secondary.current_lba = self.primary.backup_lba
254 286
        self.secondary.last_usable_lba = lba_count - 34  # LBA-34
......
259 291
        # Copy the new partition table back to the device
260 292
        with open(self.disk, "wb") as d:
261 293
            d.write(self.mbr.pack())
262
            d.write(struct.pack("%ss" % BLOCKSIZE, '\x00' * BLOCKSIZE))
263
            d.seek(BLOCKSIZE)
264 294
            d.write(self.primary.pack())
295
            d.write('\x00' * (BLOCKSIZE - self.primary.size()))
265 296
            d.seek(self.secondary.part_entry_start * BLOCKSIZE)
266 297
            d.write(self.part_entries)
267 298
            d.seek(self.primary.backup_lba * BLOCKSIZE)
268
            d.write(struct.pack("%ss" % BLOCKSIZE, '\x00' * BLOCKSIZE))
269
            d.seek(self.primary.backup_lba * BLOCKSIZE)
270 299
            d.write(self.secondary.pack())
300
            d.write('\x00' * (BLOCKSIZE - self.secondary.size()))
271 301

  
272 302
        return new_size
273 303

  
274 304
if __name__ == '__main__':
275 305
    ptable = GPTPartitionTable(sys.argv[1])
276 306

  
277
    print "MBR:"
278
    ptable.mbr.show()
279
    print
280
    print "Primary partition table:"
281
    ptable.primary.show()
282
    print
283
    print "Secondary partition table:"
284
    ptable.secondary.show()
307
    print "MBR:\n%s" % ptable.mbr
308
    print "Primary partition table:\n%s" % ptable.primary
309
    print "Secondary partition table:\n%s" % ptable.secondary
285 310

  
286 311
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :

Also available in: Unified diff